Implementing your own design
This is a reference for 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 a 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
filtersandtagsvaries 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.
{% let x = 10 %}
{% if x > 5 %}
{% if product != blank %}
<div data-product="{{ product.handle }}">{{ product.title }}</div>
{% endif %}
{% endif %}
Comments
Comments are intended for documentation, but they can also be useful during development and testing.
{% 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).
{% 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 inherit their value from the parent scope but cannot modify it.
{% 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.
{% 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
if, unless and case.
| 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 |
if
Conditional code blocks are executed when using 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 %}
unless
You can also invert the expression by using an unless statement; sometimes that makes more sense.
{% unless x > 10 %}
{{ x }} is less than 10
{% elsunless x > 5 %}
{{ x }} is smaller than 5
{% else %}
{{ x }} is larger than 6
{% endunless %}
case
When you need multiple statements on the same variable you may use a case when 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.
{% 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.
{% 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.
{% for i in (1..10) offset:5 limit:3 %}
{{ i }}
{% endfor %}
TIP Use
limitandoffsetparameters to constrain the looping.
You can reverse iterate by including the reversed parameter.
{% 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.
{{ '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}}.
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.
{% 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!
Read guide for more information about template objects
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.
{{ 'asset.js' | script_tag }}
By adding the .liquid extension you can additionally use server-side scripting.
<script src="{{ 'asset.js' | asset_url }}"></script>
assets.js.liquid
var backgroundColor = "{{ settings.pageBackgroundColor | def: '#fff' }}";
assets.js.liquid file
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.
{% for var i in (1..10) %}
<div class="test">
{% include 'mysnippet' with i %}
</div>
{% endfor %}
{% comment %}
The variable i passed above is called 'mysnippet' here
{% endcomment %}
i is set to: {{ mysnippet }}
mysnippet.liquid
TIP You can also pass named variables to snippets.
{% 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.
{% 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.
{% 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.
{{ 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.
{{ 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.
{{ 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.
{% layout 'none' %}
{{ product | json }}
After saving the file you can call the endpoint with a javascript ajax GET request.
fetch("{{product | url}}?template=json").then(res => {
console.log(res.json());
});
TIP You select the name of the template by using
?template=jsonurl-option.


