Skip to content
Oh My Web

Shopify cheat sheet: Liquid, theme files and store setup.

A one-page Shopify cheat sheet: Liquid objects, tags and filters, theme files, CLI commands and the store settings worth checking before launch. Updated 2026.

By Mohammed NadeemUpdated First published

Illustration of an open reference notebook with lines of code beside a shopping bag and a price tag.

This is the page we keep open while working on Shopify stores. It covers the Liquid that themes are written in, where each theme file lives, the CLI commands, and the store settings we check before any launch. It is written for Online Store 2.0 themes, which is every theme in the Shopify Theme Store today, and it was last checked against Shopify's own documentation in September 2026.

If you only need one thing from it: prices are stored in cents, loops stop at 50, and images need a width.

Liquid in one minute

Liquid is Shopify's template language. It runs on Shopify's servers, reads your store's data, and outputs the HTML the customer receives. There are three pieces of syntax to learn, and everything else is vocabulary.

SyntaxWhat it doesExample
{{ … }}Outputs a value{{ product.title }}
{% … %}Runs logic, outputs nothing itself{% if product.available %}
|Passes a value through a filter{{ product.price | money }}
{%- … -%}Same as above, with surrounding whitespace trimmed{%- if cart.item_count > 0 -%}
{% if product.available %}
  <h1>{{ product.title }}</h1>
  <p>{{ product.price | money }}</p>
{% else %}
  <p>Sold out</p>
{% endif %}

Two rules catch everyone once. Conditions with and and or are evaluated right to left, and Liquid has no parentheses to change that, so a complicated condition is better written as nested if tags. And contains works on strings and on arrays of strings, not on arrays of objects.

Objects you will use every day

An object is data Shopify hands to your theme. Which objects exist depends on the page: product exists on a product page, collection on a collection page, and a few are available everywhere.

ObjectWhereProperties you will reach for
productProduct pages, and anywhere you loop over productstitle, handle, url, price, compare_at_price, available, vendor, type, tags, variants, selected_or_first_available_variant, featured_media, media, metafields
variantInside product.variantsid, title, price, sku, available, inventory_quantity, option1option3
collectionCollection pagestitle, handle, url, description, products, products_count, all_products_count, filters, sort_by
cartEverywhereitem_count, total_price, items, note
customerEverywhere, empty when nobody is logged infirst_name, email, tags, orders_count
shopEverywherename, url, currency, email
requestEverywherepage_type, path, locale, design_mode (true inside the theme editor)
routesEverywhereroot_url, cart_url, account_url, search_url, collections_url
settingsEverywhereEvery theme setting defined in config/settings_schema.json
section, blockInside sections and blockssection.settings, section.id, block.settings, block.type, block.shopify_attributes

You can also reach specific items from any page by their handle, the lowercase, hyphenated version of the title that also appears in the URL. A product called "Blue T-Shirt" has the handle blue-t-shirt.

LookupReturnsWatch out for
all_products['blue-t-shirt']One productLimited to 20 unique handles per page. More than that, use a collection
collections['summer']One collectionIts products still stop at 50 without pagination
pages['about-us']One pageReturns empty, not an error, if the handle is wrong
linklists['main-menu'].linksA navigation menu's linksThe handle comes from the menu, not its title
metaobjects.testimonials.valuesEvery entry of a metaobject typeSee the metafields section below

Tags

Tags are the logic. Every block tag has an end tag: if and endif, for and endfor.

TagUse it for
if, elsif, else, unlessConditions. unless is if not
case, whenComparing one value against several
forelseLoops, with an else that renders when the array is empty. Accepts limit:, offset: and reversed
break, continueLeaving a loop early, or skipping an item
cycleAlternating values on each iteration, such as row classes
paginateSplitting a collection or search results into pages
assign, captureCreating a variable, or capturing a block of output into one
renderOutputting a snippet, with its own isolated variables
section, sectionsOutputting one section, or a section group such as the header
content_for 'blocks'Outputting a section's theme blocks in the merchant's order
formProduct, cart, contact and customer forms with the right hidden fields
schema, stylesheet, javascriptA section's settings, and CSS and JS scoped to it
liquid, echoSeveral tags inside one delimiter, without repeating {% %} on every line
comment, raw, docCommenting out code, outputting Liquid literally, and documenting a snippet's parameters

Inside a for loop, the forloop object tells you where you are: forloop.index counts from 1, forloop.index0 from 0, and forloop.first, forloop.last and forloop.length do what they say.

A for loop stops after 50 iterations. This is the most common reason a collection page "is missing products". Wrap it in paginate, which takes a page size from 1 to 250:

{% paginate collection.products by 24 %}
  {% for product in collection.products %}
    {% render 'product-card', product: product %}
  {% else %}
    <p>No products in this collection yet.</p>
  {% endfor %}

  {{ paginate | default_pagination }}
{% endpaginate %}

Use render, not include. include is deprecated because it leaks every variable into the snippet, which is how a harmless edit in one snippet breaks another. With render, a snippet only sees what you pass it:

{% render 'price', product: product, show_compare: true %}

Filters

A filter changes a value on its way out. Filters chain from left to right: {{ product.title | downcase | truncate: 30 }}.

GroupFiltersExample
Moneymoney, money_with_currency, money_without_currency, money_without_trailing_zeros{{ product.price | money }} gives $10.00, from a stored 1000
Imagesimage_url, image_tag{{ product | image_url: width: 800 | image_tag }}
Theme assetsasset_url, stylesheet_tag, script_tag{{ 'theme.css' | asset_url | stylesheet_tag }}
Textupcase, downcase, capitalize, truncate, truncatewords, strip_html, escape, handleize, append, prepend, replace, remove, split{{ article.content | strip_html | truncatewords: 30 }}
Arrayssize, first, last, join, map, where, sort, sort_natural, uniq, reverse, concat, compact{{ collection.products | where: 'available' | size }}
Mathsplus, minus, times, divided_by, modulo, round, ceil, floor, at_least, at_most{{ 7 | divided_by: 2 }} gives 3, because both numbers are integers
Datesdate{{ article.published_at | date: '%-d %B %Y' }}
Translationst{{ 'products.product.add_to_cart' | t }}
Fallbacksdefault{{ product.vendor | default: 'Our studio' }}
Linkswithin, link_to{{ product.url | within: collection }}
Metafieldsmetafield_tag, metafield_text{{ product.metafields.custom.care_guide | metafield_tag }}
Datajson{{ product | json }}, for handing data to JavaScript

Three of these deserve a warning.

  • Money. Prices are stored in the currency's smallest unit, so 1000 is $10.00. Printing product.price without money shows the raw number.
  • Images. image_url returns an error unless you give it a width or a height, up to 5760 pixels. The old img_url filter still appears in tutorials; it is deprecated. Pipe image_url into image_tag and Shopify writes the srcset for you.
  • Division. divided_by rounds down when both numbers are integers. Divide by 2.0 if you need the decimal.

Metafields and metaobjects

Metafields are custom fields on products, collections, customers and the rest: a care guide, a size chart, a list of related products. Metaobjects are your own content types, such as testimonials or store locations, with as many entries as you like.

{% # The formatted value, ready to print %}
{{ product.metafields.custom.care_guide }}

{% # The raw typed value %}
{{ product.metafields.custom.care_guide.value }}

{% # A list of product references %}
{% for related in product.metafields.custom.related_products.value %}
  {% render 'product-card', product: related %}
{% endfor %}

{% # Every entry of a metaobject type %}
{% for testimonial in metaobjects.testimonials.values %}
  <blockquote>{{ testimonial.quote }}</blockquote>
{% endfor %}

The .value part is what trips people up. Without it you get the metafield itself, formatted for display; with it you get the underlying data, which is what you loop over. And before writing any of this by hand, check whether the theme editor's dynamic sources already let you connect the metafield to a section setting without code.

Where every theme file lives

FolderHoldsNotes
layout/theme.liquid, the wrapper around every pageMust output content_for_header in the head and content_for_layout in the body
templates/One JSON file per page type: product.json, collection.json, index.jsonJSON lists which sections appear, so merchants rearrange them in the editor. Alternates such as product.gift.json render with ?view=gift
sections/Section files, plus section groups such as header-group.json and footer-group.jsonA section has its own settings in its schema tag
blocks/Theme blocks that any section can acceptCan nest inside each other, up to eight levels
snippets/Reusable fragmentsOutput with render
assets/CSS, JavaScript, fonts, imagesLinked with asset_url
config/settings_schema.json defines theme settings; settings_data.json holds their valuesEditing settings_data.json by hand is overwritten the next time someone saves in the editor
locales/Translation strings, such as en.default.jsonRead with the t filter

A minimal section, with a setting the merchant can change and a preset so it appears in the "Add section" list:

<div class="announcement">
  {{ section.settings.text }}
</div>

{% schema %}
{
  "name": "Announcement",
  "settings": [
    { "type": "text", "id": "text", "label": "Text", "default": "Free delivery over ₹999" }
  ],
  "presets": [{ "name": "Announcement" }]
}
{% endschema %}

Shopify CLI commands

The CLI is how theme work should happen: on your machine, in version control, against a copy of the theme rather than the live one. Install it with npm install -g @shopify/cli.

CommandDoes
shopify theme initStarts a new theme from Shopify's reference theme
shopify theme pull --store your-storeDownloads a theme from the store
shopify theme dev --store your-storeServes a local preview at http://127.0.0.1:9292 with your real store data, reloading as you save
shopify theme checkLints the theme for errors, deprecated tags and performance problems
shopify theme push --unpublishedUploads the theme as a new, unpublished copy
shopify theme pushOverwrites an existing theme. Choose carefully which one
shopify theme shareUploads an unpublished copy and gives you a preview link to send to someone
shopify theme publishMakes a theme live

Run theme check before every push. It catches the deprecated include and img_url above, missing translation keys, and markup that blocks rendering.

Store URLs worth knowing

Every Shopify store answers the same set of URLs, which is useful for testing, for building links, and for debugging someone else's store.

URLReturns
/collections/allEvery product in the store
/products/blue-t-shirt.jsThat product as JSON, for JavaScript
/cart.jsThe current cart as JSON
/cart/add.js, /cart/change.js, /cart/clear.jsThe cart endpoints themes use to add, update and empty the cart
/cart/12345:1,67890:2A cart permalink: a link that fills the cart with those variant IDs and quantities
/discount/SPRING10Applies a discount code to the visitor's session, then shows the home page
/products/blue-t-shirt?view=giftThe product through the alternate template product.gift.json
/search?q=linenSearch results
/sitemap.xmlThe sitemap Shopify generates and keeps up to date for you
/robots.txtGenerated too, and editable through a robots.txt.liquid template if you really need to

Store setup checklist

For owners rather than developers: the settings we check on every store before it takes its first order. The labels are the ones in the Shopify admin today.

Settings → General. Store name, contact email, address and the store currency. The currency is hard to change once orders exist.

Settings → Payments. Shopify Payments is the simplest option where it is offered. It was not available to stores based in India at the time of writing, so Indian stores connect a third-party provider such as Razorpay or PayU. Place a real test order whichever you use.

Settings → Markets. Which countries you sell to, and in which currencies and languages. Each market can have its own prices and domain.

Settings → Shipping and delivery. Shipping profiles, zones and rates. Check that every product sits in a profile that ships where you sell.

Settings → Taxes and duties. Shopify calculates the tax, but registering and filing are still your responsibility, including GST for Indian stores.

Settings → Checkout. Checkout fields, customer accounts and the checkout's look in the checkout editor. checkout.liquid has been retired: the checkout pages in 2024, and the thank-you and order status pages in August 2025. Customisation now means checkout UI extensions, usually through an app. Shopify Scripts were scheduled to stop on 30 June 2026, with Shopify Functions as the replacement, so check that any discount or shipping logic you rely on has moved.

Settings → Policies. Refund, privacy, terms and shipping policies. Payment providers expect them, and customers look for them.

Settings → Notifications. Order confirmation and shipping emails, and the address they are sent from.

Settings → Customer events. Tracking pixels live here, rather than pasted into the theme. Google Analytics connects through Shopify's Google & YouTube app.

Online Store → Preferences. The home page title and meta description, and the password page. Removing the password is the last step before launch.

Getting found on Google

Shopify does more of this for you than most platforms, so the work is mostly not undoing it.

  • Write each product's own listing. Every product, collection and page has a search engine listing section at the bottom of its edit screen for the title and description Google shows. The defaults are your product title and the first lines of the description.
  • Keep the redirect when you change a handle. Shopify offers to create a URL redirect when you edit a handle. Leave it ticked, or every link to the old URL breaks.
  • Describe your images. Every image in the admin has a place for alt text. Screen readers use it, and so does image search.
  • Leave the canonical tag alone. Themes print {{ canonical_url }} in theme.liquid, which tells Google that /collections/shirts/products/blue-t-shirt and /products/blue-t-shirt are the same page.
  • Submit the sitemap once. /sitemap.xml goes into Google Search Console, and Shopify keeps it current after that.

Common mistakes

  1. Editing the live theme. Duplicate it first, or work locally with the CLI. A typo in theme.liquid takes the whole storefront down.
  2. Too many apps. Each one is usually a monthly fee and a script on every page, and some leave code in the theme after you uninstall them. Review the list every quarter.
  3. Printing prices without money, and wondering why a ₹1,499 product shows 149900.
  4. Looping a collection without paginate, and losing everything after the 50th product.
  5. Following old tutorials. include, img_url and checkout.liquid all still appear in search results. If a snippet uses them, it predates the current platform.
  6. Changing handles without redirects. Every changed URL without a redirect is a broken link in Google and in every email you ever sent.

Most of this is manageable in-house. Where it stops being so is product data structure, checkout changes, integrations with the other systems you run, and custom theme code you would have to carry through every theme update. That is the work our Shopify development team does. If the store is built and simply needs someone keeping apps, theme updates and speed in order, Shopify store maintenance covers that from ₹4,999 / $79 a month.

Questions

What is a Shopify cheat sheet?

A one-page reference for the things you look up most while working on a Shopify store: the Liquid objects, tags and filters that themes are written in, where each theme file lives, the Shopify CLI commands, and the admin settings to check before a store goes live.

What language are Shopify themes written in?

Liquid, an open-source template language Shopify created, alongside ordinary HTML, CSS and JavaScript. Liquid runs on Shopify's servers and outputs the HTML the customer receives, so it can read store data such as products and the cart, but it cannot run arbitrary code.

Why does my Liquid loop only show 50 products?

A for loop in Shopify Liquid stops after 50 iterations. To show more products from a collection, wrap the loop in the paginate tag, which accepts a page size between 1 and 250.

Why are my Shopify prices showing as large numbers?

Liquid stores prices in the currency's smallest unit, so a $10.00 product comes through as 1000. Pass the value through the money filter, or money_with_currency, and Shopify formats it using the store's currency settings.

Can I edit my theme code without breaking the live store?

Yes. Duplicate the theme in the admin, or pull it locally with the Shopify CLI, and work on the unpublished copy. shopify theme dev gives you a local preview against your real store data, and you only publish once the copy is tested.

When is it worth hiring a Shopify developer?

When the change touches product data structure, the checkout, an integration with another system, or theme code you would have to maintain through future theme updates. Settings, content and app choices are usually fine to handle yourself.

Want a second pair of eyes on your store?

Book a 30-minute call