Implementing your own design
This is reference on how to use Liquid templating when developing your own design templates.
More on theme files and file structure
The Code Editor
When working with templates you need to have the code editor open along with a preview of the shop.
You can toggle the code editor by clicking on the Editor button in Theme settings and open the preview in a separate window or browser tab.
When you save any changes the preview is reloaded automatically.
Liquid templates
All files ending with .liquid can contain code that is evaluated server-side. Liquid templates are based on Django which was created around 2003.
There’s lot of information on the internet on how to develop using Liquid so we’ll just skim through the basics.
NOTE The implementation of Liquid
filtersandtagsvary somewhat between all engines.
Code blocks
Code is evaluated server side in code blocks surrounded by handlebars {{ ... }} or {% ... %}.
The first form is used when you want to output text, the latter form is used when you want to evaluate code without any output.
Example of code evaluation and output
{% let x = 10 %}
{% if x > 5 %}
{% if product != blank %}
<div data-product="{{ product.handle }}">{{ product.title }}</div>
{% endif %}
{% endif %}
Comments
Comments are meant for documentation but can also be useful when developing and testing.
Example of a comment
{% comment %}
This is a comment block which also can be used to turn of blocks of code.
{% let x = 1000 %} // this line is not evaluated
{% endcomment %}
Raw
The raw keyword is useful for outputting text that could otherwise interfere with liquid.
Variable assignment
Variables can be created in two ways.
- Use
{% assign <variable> = <assignment> %}when declaring variables that should have global scope. - Use
{% let <variable> = <assignment> %}when declaring variables that should have local scope.
NOTE You can also create variables/objects in for-loop declarations (and other code constructs).
Example of local and global variables
{% let localX = 123 %}
{% assign globalX = 123 %}
Local variables are only accessible inside the code block and file they are declared in. Global variables are accessible from the point of declaration to the end of execution.
NOTE let assignments inherits its value from parent scope but cannot modify it.
Example comparison let and assign
{% let x = 10 %}
{% assign y = 10 %}
{% for z in (10..1) %}
x = {{ x }}
y = {{ y }}
{% let x = z %}
{% assign y = z %}
{% endfor %}
x is {{ x }} // x = 10
y is {{ y }} // y = 1
Capture blocks
A capture block allows you to assign rendered output into a variable.
It is handy for combining strings and variables when rendering html output.
Example of a capture block
{% let x = 10 %}
{% let y = 20 %}
{% capture attrs %}href="link-{{x | plus:y}}.html" data-from="{{x}}" data-to="{{y}}"{% endcapture %}
<a {{ attrs }}>This is a link<a/>
Control flow
Conditional code blocks are executed when using if elsif else statements.
Example of if, elsif, else statements
{% if x > 10 %}
{{ x }} is larger than 10
{% elsif x > 5 %}
{{ x }} is larger than 5
{% else %}
{{ x }} is less than 6
{% endif %}
Logical operators
| Operator | Operation |
|---|---|
| == | equals |
| != | does not equal |
| > | greater than |
| < | less than |
| >= | greater than or equal to |
| <= | less than or equal to |
| or | logical or |
| and | logical and |
You can also invert the expression by using an unless statement, sometimes that makes more sense.
Example of an unless statement
{% unless x > 10 %}
{{ x }} is less than 10
{% elsunless x > 5 %}
{{ x }} is smaller than 5
{% else %}
{{ x }} is larger than 6
{% endunless %}
When you need multiple statements on the same variable you may use a case when block.
Example of a case block
{% case x %}
{% when 10 %}
x is 10
{% when 9 %}
x is 9
{% else %}
x is not 10 or 9
{% endcase %}
For-loops
Iterating code can be done using for loop blocks.
For loops can iterate over arrays, hashes, and ranges of integers.
Example of a for loop iterating over an array
{% for product in collection.products %}
{{product.title}}
{% endfor %}
NOTE A local variable
productwas assigned from theproductsarray iteration.
You can also iterate over a range of numbers instead of arrays.
Example of a for loop using break
{% for i in (1..10) %}
{% if i > 8 %}
{% break %}
{% else %}
{{ i }}
{% endif %}
{% endfor %}
TIP Use
breakto exit the loop before completion.
You may also use limit and offset arguments.
Example of a for loop using limit and offset
{% for i in (1..10) offset:5 limit:3 %}
{{ i }}
{% endfor %}
TIP Use
limitandoffsetparameters to constraint the looping.
You can reverse iterate by including the reversed parameter.
Example of a reversed for loop iteration
{% for i in (1..10) reversed %}
{{ i }}
{% endfor %}
For loops create some additional variables that can be handy.
Forloop variables
| Variable | Description |
|---|---|
| forloop.index | index of the current iteration, starting with 1 |
| forloop.index0 | zero based index of the current iteration, starting with 0 |
| forloop.length | number of iterations |
| forloop.rindex | remaining iterations, ending on 1 |
| forloop.rindex0 | remaining iterations, ending on 0 |
| forloop.first | true on first iteration |
| forloop.last | true on last iteration |
Filters
Filters can transform variables (input) and can be used for performing math operations, string conversions, array manipulations and much more.
A filter is applied by using a pipe character | followed by the filter name an optional colon : with one or more comma separated arguments.
Example using a filter
{{ 'Hello World' | slice: 1,5 | uppercase }}
List of liquid filters
Template objects
When working with templates the system creates objects that you can use depending on what context you are in.
Let’s assume you are working with the product template product.liquid (in a ‘product context’), when using that template you have access to the current product object and you can inspect it using {{ product | print_r}} or {{ product | inspect}}.
Some objects are always available (but lazy loaded on access) such as collections and products but there’s also a customer object which only exists when a customer has logged in.
These objects are made available server side when using
liquidcode but can be made available client sidejsusing thejsonfilterconst p = {{ product | json}};.
Example of using a template object
{% comment %}
We can reference a product object since we are in a product context.
{% endcomment %}
{% assign productTitle = product.title | upcase %}
{% assign onSale = false %}
{% if product.compareAtPrice > product.price %}
{% assign onSale = true %}
{% endif %}
{{productTitle}} is {% unless onSale %}not {% endunless %}on sale!
If you want to see all properties of an
objectyou can do a recursive print out using{{ <object> | print_r }}or{{ <object> | inspect }}.
Global objects
There are global objects that can be reached from all contexts.
CMS
| Object | Description |
|---|---|
| {{ site }} | Site information |
| {{ request }} | Site visitor request object |
| {{ settings }} | All the theme settings saved in settings_data.json file |
| {{ pages }} | Lists all pages |
| {{ blogs }} | Lists all blogs |
| {{ authors }} | Lists all authors |
| {{ navigation }} | Navigation lists |
| {{ countries }} | List of all countries |
| {{ lang }} | Current language |
eCommerce
| Object | Description |
|---|---|
| {{ shop }} | Shop information |
| {{ collections }} | Lists of all product collections |
| {{ products }} | List of all products |
| {{ vendors }} | Lists of all vendors |
| {{ customer }} | Logged in customer |
| {{ currency }} | Current currency |
Site object {{ site }}
You can always print the site object with {{ site | print_r }}
| Property | Description |
|---|---|
| {{ site.id }} | The unique identifier of the site |
| {{ site.name }} | Name of the site, intended mainly for internal purposes. |
| {{ site.lang }} | Default site language |
| {{ site.domain }} | The main domain |
| {{ site.theme }} | The id of the published theme |
| {{ site.contact }} | The contact point given in the site settings for the site contact |
| {{ site.createdAt }} | Date when site was created |
| {{ site.meta.h1 }} | The H1 heading for the site, given in site settings |
| {{ site.meta.title }} | The site title |
| {{ site.meta.description }} | Site description |
| {{ site.meta.keywords }} | Site keywords |
| {{ site.organisation }} | Organisation as stated in the site settings |
| {{ site.location }} | Location as stated in the site settings |
Request object {{ request }}
The request object will specify the host, origin, path, query, referer and scheme of the current request.
| Property | Description |
|---|---|
| {{ request.host }} | The domain name |
| {{ request.origin }} | Origin of the content, different from host if the site is run headless. |
| {{ request.path }} | Current path |
| {{ request.query }} | The query of the request, for example tags=CMS if a tag is chosen on a blog |
| {{ request.referer }} | Address of the previous page |
| {{ request.remote }} | The IP of the site visitor |
| {{ request.scheme }} | http or https |
Using the request information it is possible to add in user-specific content, for example if the referer contains “google” the content can be rendered adapted to this.
Example, showing promo if visitor comes from google
{% if request.referer contains "google" %}
<aside>
<div class="promo brand">
<h2>Want to create websites?</h2>
<a class="shiny-cta" href="/features">Learn more</a>
</div>
</aside>
{% endif %}
Context dependent objects
Context dependent objects, the name of the object is the same as the current context i.e. page,product, cart, collection.
CMS
| Object | Theme template |
|---|---|
| {{ page }} | page.liquid |
| {{ blog }} | blog.liquid |
| {{ article }} | article.liquid |
eCommerce
| Object | Template |
|---|---|
| {{ product }} | product.liquid |
| {{ collection }} | collection.liquid |
| {{ cart }} | cart.liquid |
| {{ checkout }} | checkout.liquid |
Referencing outside the context
It is possible to reach the objects outside the context of the object.
Using theme settings
With settings it is possible to reach a specific blog/collection/product/page/article from the theme settings data stored in the settings object.
{% assign collection = collections[settings.homepageCollection.id] %}
Using handle privacy
{{ pages['privacy'].html }}
Referencing a product from products
The products object will contain all the products in an array, it is possible to pick a product from the array by specifying the position, with 0 being the first. products[0] will pick the first product.
Theme assets
Text files uploaded to a theme can be edited using the code editor.
Binary files however, are uploaded to the themes media directory and are not editable.
Asset files are referenced by name using the asset_url filter like this {{ 'icons.svg' | asset_url }}.
Javascript
You can upload any javascript file to the assets folder and include that in your theme layout.
Example including a javascript tag
{{ 'asset.js' | script_tag }}
By adding the .liquid extension you can additionally use server-side scripting.
Example of including assets.js.liquid
<script src="{{ 'asset.js' | asset_url }}"></script>
Example of a assets.js.liquid file
var backgroundColor = "{{ settings.pageBackgroundColor | def: '#fff' }}";
TIP Set default values using the
deffilter to avoid errors caused by missing values.NOTE Strings are returned as plain text so you need to include the surrounding apostrophes
".
Template snippets
Template snippets are handy for reusing blocks of code and included as partials from your theme files.
You create a snippet by clicking on the create link found under the Snippets folder in the editor.
To use a snippet you include the file with an optional argument that is passed to the snippet as a variable under its own name.
Example including a snippet
{% for var i in (1..10) %}
<div class="test">
{% include 'mysnippet' with i %}
</div>
{% endfor %}
Example of a snippet file called mysnippet.liquid
{% comment %}
The variable i passed above is called 'mysnippet' here
{% endcomment %}
i is set to: {{ mysnippet }}
TIP You can also pass named variables to snippets.
Example of passing named variables
{% for var i in (1..10) %}
<div class="test">
{% include 'mysnippet' i:i, show:true %}
</div>
{% endfor %}
NOTE Two variables are created and assigned values which can then be accessed by the snippet.
You can also combine with and named variables like this.
Example of using with and named variables
{% for var i in (1..10) %}
<div class="test">
{% include 'mysnippet' with i show:true %}
</div>
{% endfor %}
TIP It’s good practice to set default values in the snippet.
Example of snippet variables and using default values
{% comment %}
The variable i passed above is called 'mysnippet' here and a named variable called 'show' has been created if set.
{% endcomment %}
{% let my_i = mysnippet | def:0 %}
{% let my_show = show | def:false %}
Image rescaling
Image rescaling is an important feature for theme developers when creating designs that adapts to different screen sizes, commonly known as responsive design. Cradle offers server side rescaling of images, with some tweaks, so that you can get optimal performance from your design.
Supported image files: jpg, png, webp and gif. But please note that gifs loose their frames.
| Sizing method | Liquid | File output |
|---|---|---|
| Rescale Specifying both the width and height the image will scale down to these values. |
{{ item.image | asset_img_url:<width>,<height> }} |
filename_<width>_<height>.jpg |
| Keep aspect ratio Image will keep aspect ratio if specifying height or width and leaving the other value to 0 |
{{ item.image | asset_img_url:<width>,0 }} |
filename_<width>_0.jpg or filename_0_<height>.jpg |
| Crop image With cropping, parts of the image will be removed to fit the specified size. |
{{ item.image | asset_img_url:<width>,<height> ,'cropped' }} |
filename_<width>_<height>_cropped.jpg |
| Whitefit Image keep aspect ratio and the remaining space will be filled with white |
{{ item.image | asset_img_url:<width>,<height> , 'whitefit' }} |
filename_<width>_<height>_whitefit.jpg |
Examples of rescaled images
| Image scaled to 200px wide, keeping the aspect ratio | Image cropped to 200px X 200px, cropped | Image scaled to 200px X 200px with whitefit |
|---|---|---|
![]() |
![]() |
![]() |
Product images
Product images can be rescaled using the following code snippets.
Example of rescaling a products featured image to 200px x 200px
{{ product.featuredImage | product_img_url:200,200 }}
The product_img_url filter takes three arguments, width, height and an additional cropped or whitefit (optional).
When the width or height is zero the image is scaled to maintain its original aspect ratio.
Example of rescaling a product image to a fixed height of 200px
{{ product.featuredImage | product_img_url:0,200 }}
The above code would render an image with a fixed height of 200 pixels.
When rescaling with a fixed width and height it is useful to center crop the image.
That can be done by adding cropped argument.
Example of cropping a product image to 200px X 200px
{{ product.featuredImage | product_img_url:200,200,'cropped' }}
If cropping is not an option you may try the whitefit argument instead which maintains the aspect ratio by including a white border.
Custom endpoints
You can create your own ajax endpoints by returning json in a template instead of html.
Start by adding a new template in the code editor by clicking on the Create Template link.
As an example, if you want to create a product endpoint you select the product template and call it json.
You then get a new template file called product.json.liquid.
Example of a custom product json endpoint
{% layout 'none' %}
{{ product | json }}
After saving the file you can call the endpoint with a javascript ajax GET request.
Example of calling a custom product json endpoint using fetch
fetch("{{product | url}}?template=json").then(res => {
console.log(res.json());
});
TIP You select the name of the template by using
?template=jsonurl-option.


