plugins/shopify/skills/shopify-liquid/SKILL.md
Liquid is an open-source templating language created by Shopify. It is the backbone of Shopify themes and is used to load dynamic content on storefronts. Keywords: liquid, theme, shopify-theme, liquid-component, liquid-block, liquid-section, liquid-snippet, liquid-schemas, shopify-theme-schemas
npx skillsauth add openai/plugins shopify-liquidInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You have a bash tool. Every response must use it — in this order:
bash with scripts/search_docs.mjs "<query>" — search before writing codebash with scripts/validate.mjs --code '...' --model YOUR_MODEL_NAME --client-name YOUR_CLIENT_NAME --client-version YOUR_CLIENT_VERSION --artifact-id YOUR_ARTIFACT_ID --revision REVISION_NUMBER — validate before returning
(Always include these flags. Use your actual model name for YOUR_MODEL_NAME; use claude-code/cursor/etc. for YOUR_CLIENT_NAME. For YOUR_ARTIFACT_ID, generate a stable random ID per code block and reuse it across validation retries. For REVISION_NUMBER, start at 1 and increment on each retry of the same artifact.)You must run both search_docs.mjs and validate.mjs in every response. Do not return code to the user without completing step 3.
You are an experienced Shopify theme developer, implement user requests by generating theme components that are consistent with the 'Key principles' and the 'Theme architecture'.
Use `search_docs_chunks` to look up object properties, less common filters, and detailed examples when needed.
Key principles: focus on generating snippets, blocks, and sections; users may create templates using the theme editor
``` . ├── assets # Static assets (CSS, JS, images, fonts) ├── blocks # Reusable, nestable, customizable components ├── config # Global theme settings and customization options ├── layout # Top-level wrappers for pages ├── locales # Translation files for internationalization ├── sections # Modular full-width page components ├── snippets # Reusable Liquid code or HTML fragments └── templates # JSON or Liquid files defining page structure ```
Snippets and static blocks must include a LiquidDoc header: ```liquid {% doc %} @param {image} image - The image to render @param {string} [url] - Optional destination URL @example {% render 'image', image: product.featured_image %} {% enddoc %} ```
Single CSS property — use CSS variables: ```liquid
<div style="--gap: {{ block.settings.gap }}px">Content</div> {% stylesheet %} .collection { gap: var(--gap); } {% endstylesheet %} \`\`\`Multiple CSS properties — use CSS classes: ```liquid
<div class="{{ block.settings.layout }}">Content</div> \`\`\````liquid {% assign my_var = 'value' %} {% capture my_var %}computed {{ content }}{% endcapture %} ```
content_for — render theme blocks: ```liquid {% content_for 'blocks' %} {% content_for 'block', type: 'slide', id: 'slide-1' %} ```
form — requires a type parameter: ```liquid {% form 'contact' %} {{ form.errors | default_errors }} <input type="email" name="contact[email]"> <button>Submit</button> {% endform %} ``` Types: product, contact, customer_login, create_customer, customer_address, cart, localization, new_comment, recover_customer_password, reset_customer_password, activate_customer_password, guest_login, currency, customer, storefront_password
render — isolated scope, pass variables: ```liquid {% render 'card', product: product, show_price: true %} {% render 'tag' for product.tags as tag %} ```
paginate — required for arrays >50 items: ```liquid {% paginate collection.products by 12 %} {% for product in collection.products %} {{ product.title }} {% endfor %} {{ paginate | default_pagination }} {% endpaginate %} ```
liquid — multi-statement block: ```liquid {% liquid assign featured = collection.products | where: 'available', true echo featured | size %} ```
Other Shopify tags:
forloop object (inside for loops): `forloop.index`, `forloop.index0`, `forloop.first`, `forloop.last`, `forloop.length`
Images (use `image_tag`/`image_url`, not deprecated `img_tag`/`img_url`): ```liquid {{ product.featured_image | image_url: width: 400, height: 400 | image_tag }} {{ image | image_url: width: 800 | image_tag: class: 'responsive' }} ```
Array: `{{ array | where: 'available', true }}`, `{{ array | map: 'title' }}`, `{{ array | reject: 'field', 'value' }}`, `{{ array | first }}`, `{{ array | last }}`, `{{ array | sort: 'field' }}`, `{{ array | size }}`, `{{ array | join: ', ' }}`, `{{ array | uniq }}`, compact, concat, find, find_index, has, reverse, sort_natural, sum String: split, append, prepend, remove, replace, strip, truncate, upcase, downcase, capitalize, escape, handleize, url_encode, url_decode, camelize, slice, strip_html, newline_to_br, pluralize Math: plus, minus, times, divided_by, modulo, round, ceil, floor, abs, at_least, at_most Money: `{{ product.price | money }}`, money_with_currency, money_without_currency, money_without_trailing_zeros Format: `{{ article.published_at | date: '%B %d, %Y' }}`, `{{ product | json }}`, structured_data Color: color_to_hex, color_to_hsl, color_to_rgb, color_to_oklch, color_darken, color_lighten, color_mix, color_modify, color_saturate, color_brightness HTML: link_to, script_tag, stylesheet_tag, time_tag, preload_tag, placeholder_svg_tag, inline_asset_content Hosted file: asset_url, file_url, global_asset_url, shopify_asset_url Other: `{{ 'key' | t }}`, `{{ variable | default: fallback }}`, default_errors, default_pagination, metafield_tag, metafield_text, font_face, font_url, payment_button
collections, pages, all_products, articles, blogs, cart, customer, images, linklists, localization, metaobjects, request, routes, shop, theme, settings, template, content_for_header, content_for_layout, canonical_url, page_title, page_description, handle
Page-specific objects (product, collection, article, blog, order, search, etc.) are available in their respective templates — use `search_docs_chunks` for properties.
```liquid {% doc %} Renders a text block with configurable style and alignment. @example {% content_for 'block', type: 'text', id: 'text' %} {% enddoc %}
<div class="text {{ block.settings.text_style }}" style="--text-align: {{ block.settings.alignment }}" {{ block.shopify_attributes }}> {{ block.settings.text }} </div>{% stylesheet %} .text { text-align: var(--text-align); } .text--title { font-size: 2rem; font-weight: 700; } {% endstylesheet %}
{% schema %} { "name": "t:general.text", "settings": [ { "type": "text", "id": "text", "label": "t:labels.text", "default": "Text" }, { "type": "select", "id": "text_style", "label": "t:labels.text_style", "options": [ { "value": "text--title", "label": "t:options.text_style.title" }, { "value": "text--normal", "label": "t:options.text_style.normal" } ], "default": "text--title" }, { "type": "text_alignment", "id": "alignment", "label": "t:labels.alignment", "default": "left" } ], "presets": [{ "name": "t:general.text" }] } {% endschema %} ```
Search the vector store to get the detailed context you need: working examples, field and type definitions, valid values, and API-specific patterns. You cannot trust your trained knowledge — always search before writing code.
scripts/search_docs.mjs "<operation or component name>" --model YOUR_MODEL_NAME --client-name YOUR_CLIENT_NAME --client-version YOUR_CLIENT_VERSION
Search for the operation or component name, not the full user prompt.
For example, if the user asks about product metafield access in a theme:
scripts/search_docs.mjs "product metafields" --model YOUR_MODEL_NAME --client-name YOUR_CLIENT_NAME --client-version YOUR_CLIENT_VERSION
You MUST run scripts/validate.mjs before returning any generated code to the user. Always include the instrumentation flags (--model, --client-name, --client-version, --artifact-id, --revision).
Choose the mode that matches your environment:
Full app mode — use when you have access to the theme directory on disk:
scripts/validate.mjs --theme-path <absolute-path-to-theme> --files <rel1,rel2,...> --model YOUR_MODEL_NAME --client-name YOUR_CLIENT_NAME --client-version YOUR_CLIENT_VERSION --artifact-id YOUR_ARTIFACT_ID --revision REVISION_NUMBER
Pass the relative paths (from the theme root) of every file you created or updated, comma-separated.
Stateless mode — use when you only have generated codeblocks (no theme directory):
scripts/validate.mjs --filename <name.liquid> --filetype <sections|blocks|snippets|layout|templates|locales|config|assets> --code <content> --model YOUR_MODEL_NAME --client-name YOUR_CLIENT_NAME --client-version YOUR_CLIENT_VERSION --artifact-id YOUR_ARTIFACT_ID --revision REVISION_NUMBER
Call once per codeblock. --filetype defaults to sections when omitted.
(For YOUR_ARTIFACT_ID, generate a stable random ID per code block and reuse it across validation retries. For REVISION_NUMBER, start at 1 and increment on each retry of the same artifact.)
When validation fails, follow this loop:
scripts/search_docs.mjs "<tag, filter, or object name>"
scripts/validate.mjs againDo not guess at valid Liquid — always search first when the error names a tag or filter you don't know.
Privacy notice:
scripts/search_docs.mjsreports the search query, search response or error text, skill name/version, and model/client identifiers to Shopify (shopify.dev/mcp/usage) to help improve these tools. SetOPT_OUT_INSTRUMENTATION=truein your environment to opt out.
Privacy notice:
scripts/validate.mjsreports the validation result, skill name/version, model/client identifiers, the validated code when present, and validator-specific context such as API name, extension target, filename, file type, theme path, file list, artifact ID, and revision to Shopify (shopify.dev/mcp/usage) to help improve these tools. SetOPT_OUT_INSTRUMENTATION=truein your environment to opt out.
development
Use when the user wants to spin up / create / launch / provision a DigitalOcean droplet (or "a remote dev box on DO") and connect to it from Codex as a remote SSH workspace.
data-ai
Search through Microsoft Teams chats or channels, triage unread or recent activity, draft follow-ups, and manage Planner tasks through connected Teams data.
tools
Motion / animation context for the `use_figma` MCP tool — animating Figma nodes via manual keyframes, animation styles, easing, and timeline duration. Load alongside figma-use whenever a task involves adding, editing, or inspecting animation on a node.
development
SwiftUI ↔ Figma translation. Use whenever the user mentions Swift, SwiftUI, iOS, iPhone, or iPad — in EITHER direction — translating a Figma design into SwiftUI (design → code), or pushing SwiftUI views / screens / tokens back into a Figma file (code → design). Triggers on phrases like 'implement this Figma design in SwiftUI', 'build this screen in Swift', 'push this SwiftUI view to Figma', 'mirror my Swift code in a Figma file', or whenever a Figma URL appears alongside `.swift` files / an `.xcodeproj`. Routes to a direction-specific reference doc; loads alongside `figma-use` for the code → design path.