Liquid template language support for the Zed editor, built for Shopify theme development.
- Syntax highlighting — Liquid tags, filters, variables, operators, literals, comments, and doc-tags
- Embedded languages — HTML, CSS, JavaScript, JSON, and YAML highlighted inside appropriate Liquid blocks
- Language server — Shopify Theme Language Server: completions, hover docs, go-to-definition, diagnostics
- Snippets — 120+ snippets for every common Liquid and Shopify theme pattern
- Document outline — navigate to
render/section/schema/for/capturelandmarks in the outline panel - Auto-indentation — block tags (
if,for,unless,case,capture, etc.) auto-indent their body - Code folding — fold any Liquid block tag (
if,for,schema,javascript,comment, and more) - Bracket matching — autoclosing brackets and quotes with Liquid-aware context
- Formatting — Prettier with
liquid-htmlparser (requires@shopify/prettier-plugin-liquidin your project)
The extension first looks for shopify on your PATH. If found, it starts the language server with:
shopify theme language-serverInstall the CLI if you don't have it:
npm install -g @shopify/cliIf Shopify CLI is not installed, the extension automatically downloads and installs @shopify/theme-language-server-node via npm on first use. No manual steps required.
Open Zed, run zed: extensions, search for Liquid, and click Install.
The Shopify Theme Language Server provides rich hover documentation for all Liquid objects. Hover over any Shopify object to see its type, description, and available properties:
product→ title, handle, description, variants, images, price_range, …collection→ title, handle, products, all_products_count, …cart→ items, total_price, item_count, …customer→ name, email, orders, addresses, …section→ id, settings, blocks, …forloop→ index, index0, first, last, length, …link→ title, url, active, links, …
Note: Hover docs work best when you open the full theme folder (not a single file). The language server uses the project structure to resolve objects and their types.
Configure via Zed's settings.json:
{
"lsp": {
"shopify-theme-ls": {
"settings": {
"shopifyLiquid": {
"onlySingleFileChecks": false,
"disabledChecks": []
}
}
}
}
}Most block-tag snippets respond to multiple prefixes. For example, typing if or {%if both expand to a full {% if %}…{% endif %} block.
| Prefix | Expands to |
|---|---|
if / {%if / {% if |
{% if condition %}…{% endif %} |
ife |
if / else |
ifee |
if / elsif / else |
ifne |
if array/collection not empty |
unless / {%unless |
unless block |
unle |
unless / else |
case / {%case |
case / when statement |
| Prefix | Expands to |
|---|---|
for / {%for |
for loop |
forl |
for loop with limit and offset |
forr |
for loop reversed |
forlr |
for loop with limit, offset, and reversed |
break |
{% break %} |
continue |
{% continue %} |
tablerow |
tablerow loop (HTML table rows) |
tablerowc |
tablerow with cols: |
cycle |
{% cycle 'a', 'b', 'c' %} |
| Prefix | Expands to |
|---|---|
assign / {%assign |
assign variable |
assignw |
assign filtered array via where: |
assignm |
assign mapped array via map: |
assignsort |
assign sorted array via sort: |
assignfirst |
assign first matching item via where: … | first |
capture / {%capture |
capture block |
increment |
increment variable |
decrement |
decrement variable |
| Prefix | Expands to |
|---|---|
{{ |
{{ variable }} |
{{f |
{{ variable | filter }} |
t |
{{ 'key' | t }} translation |
echo |
{% echo variable %} |
{{date |
| date: '%B %d, %Y' |
{{money |
| money |
{{moneyc |
| money_with_currency |
{{def |
| default: 'fallback' |
{{trunc |
| truncate: 100 |
{{esc |
| escape |
{{strip |
| strip_html |
{{handle |
| handle |
{{size |
| size |
{{up |
| upcase |
{{down |
| downcase |
{{rep |
| replace: 'from', 'to' |
{{app |
| append: 'suffix' |
{{pre |
| prepend: 'prefix' |
{{split |
| split: ',' |
{{join |
| join: ', ' |
{{nl |
| newline_to_br |
{{url |
| url_encode |
{{imgurl |
| image_url: width: 800 |
{{weight |
| weight_with_unit |
{{times / {{minus / {{plus |
math filters |
{{round / {{floor / {{ceil |
rounding filters |
{{slice |
| slice: 0, 5 |
{{compact / {{uniq / {{reverse |
array filters |
{{first / {{last |
first / last array item |
| Prefix | Expands to |
|---|---|
render / {%render |
render snippet |
renderw |
render snippet with parameter |
renderfor |
{% render 'snippet' for array as item %} |
include |
include snippet (deprecated — prefer render) |
section / {%section |
section tag |
sections |
sections tag |
paginate / {%paginate |
paginate block |
layout |
layout tag |
asset |
{{ 'file.css' | asset_url }} |
assetlink |
asset as <link> stylesheet tag |
imgtag |
responsive image via image_url + image_tag |
img-src |
responsive image with lazy loading |
scripttag |
asset as <script> tag |
cfh |
{{ content_for_header }} |
cfl |
{{ content_for_layout }} |
cfi |
{{ content_for_index }} |
| Prefix | Expands to |
|---|---|
form / {%form |
generic form tag |
form-contact |
{% form 'contact' %} |
form-cart |
{% form 'cart', cart %} |
form-login |
{% form 'customer_login' %} |
form-register |
{% form 'create_customer' %} |
form-reset |
{% form 'reset_customer_password' %} |
form-address |
{% form 'customer_address', customer.new_address %} |
form-activate |
{% form 'activate_customer_password' %} |
| Prefix | Expands to |
|---|---|
javascript / {%javascript |
{% javascript %}…{% endjavascript %} |
stylesheet / {%stylesheet |
{% stylesheet %}…{% endstylesheet %} |
style / {%style |
{% style %}…{% endstyle %} |
raw / {%raw |
{% raw %}…{% endraw %} |
comment / {%comment |
{% comment %}…{% endcomment %} |
liquid / {%liquid |
{% liquid … %} multi-statement block |
liquidb |
{% liquid … %} with two assign stubs |
| Prefix | Expands to |
|---|---|
schema / {%schema |
schema block with settings/presets |
schema-full |
schema with settings, blocks, and presets |
schema-block |
block definition inside "blocks": [] |
section-template |
full section boilerplate (HTML + schema + stylesheet + javascript) |
Use inside "settings": [] in any schema block.
| Prefix | Setting type |
|---|---|
s-text |
text |
s-textarea |
textarea |
s-richtext |
richtext |
s-inlinert |
inline_richtext |
s-number |
number |
s-check |
checkbox |
s-range |
range (min / max / step / default) |
s-select |
select with two option stubs |
s-radio |
radio with two option stubs |
s-color |
color |
s-colorbg |
color_background (gradient) |
s-img |
image_picker |
s-video |
video |
s-videourl |
video_url (YouTube / Vimeo) |
s-url |
url |
s-html |
html |
s-font |
font_picker |
s-linklist |
link_list |
s-product |
product |
s-coll |
collection |
s-blog |
blog |
s-page |
page |
s-header |
header (section divider, no value) |
s-para |
paragraph (info text, no value) |
| Prefix | Expands to |
|---|---|
prod-loop |
paginated products loop with default_pagination |
nav-loop |
navigation menu links loop |
section-block |
for block in section.blocks with case/when |
meta-key |
product.metafields.namespace.key | metafield_tag |
The Shopify Theme Language Server provides JSON completions and validation inside {% schema %} blocks automatically — no extra configuration needed.
For additional JSON Schema validation (e.g. in a standalone editor or CI), point your JSON language server at Shopify's published schema:
{
"json.schemas": [
{
"fileMatch": ["*.json"],
"url": "https://json.schemastore.org/shopify-section.json"
}
]
}The LS also validates section files in config/settings_schema.json and config/settings_data.json automatically when you open a theme folder (not a single file).
To use Tailwind CSS in a Shopify theme with this extension:
-
Install the Zed Tailwind CSS extension via zed: extensions.
-
Configure Tailwind to scan
.liquidfiles intailwind.config.js:module.exports = { content: [ './**/*.liquid', './assets/*.js', ], }
-
Tailwind completions and hover docs will now work in
.liquidfiles alongside Liquid completions from the Shopify LS.
Uses hankthetank27/tree-sitter-liquid — chosen over Shopify's own grammar because it correctly parses all block tags (if, for, render, section, schema, javascript, etc.), not just expressions.
| Liquid block | Highlighted as |
|---|---|
| Template content (outside tags) | HTML |
{% schema %} |
JSON |
{% style %} |
CSS |
{% stylesheet %} |
CSS |
{% javascript %} |
JavaScript |
| Front matter | YAML |
Liquid expressions embedded within CSS values or selectors
(--color: {{ variable }};, #id-{{ section.id }} {}) create
gaps in the combined CSS injection stream. The surrounding static
CSS tokens may show reduced or error highlighting near those points.
The Liquid tags themselves ({{ }}, {% %}) are always highlighted
correctly — only the adjacent CSS text is affected.
Workaround: when possible, keep Liquid expressions on their own lines and surround them with newlines. CSS highlighting is more stable when Liquid tags do not split CSS property values mid-token.