Why the Linter Everyone Ignores Deserves Five Minutes of Setup
Have you ever pushed a theme update, opened the storefront, and watched a section silently fail to render because of a typo in a Liquid tag? Shopify Theme Check exists to catch exactly that class of mistake before it ships — yet most theme developers either never run it or run it once, get buried under 200 warnings, and never run it again.
That's a configuration problem, not a tool problem. Shopify Theme Check is a static analyzer that reads every Liquid and JSON file in your theme and flags errors, performance issues, and deprecated patterns. Tuned correctly, it becomes a quiet safety net. Left on defaults, it's a noise machine.
This guide covers what the official docs answer badly: the real --path syntax (and why pointing it at a single file doesn't do what you think), .theme-check.yml configuration that actually reduces noise, CI integration, VS Code setup, and the CLI version bugs that make Theme Check feel broken when your code is fine. If you're newer to theme work, skim our Shopify theme development guide for beginners first — this article assumes you have a theme repo and the CLI installed.
What Shopify Theme Check Actually Does

Theme Check is a linter, not a compiler. It parses your theme's Liquid and JSON, runs a battery of named checks against the parse tree, and reports offenses — each tied to a file, a line number, and a check name like ParserBlockingJavaScript or UnusedSnippet.
It ships inside the Shopify CLI today. The original standalone tool lived in the Shopify/theme-check repository, but for modern themes you'll run it through shopify theme check — the CLI bundles the current TypeScript implementation that also powers the VS Code language server.
Linting vs. Validation: Two Different Gates
This distinction matters for troubleshooting later, so get it straight now:
- Theme Check (linting) runs when you invoke it — manually, in CI, or via your editor. It never blocks
shopify theme devorshopify theme pushby default. - Platform validation runs server-side on every file upload. A Liquid syntax error that breaks rendering gets rejected by Shopify itself, regardless of your linter config.
You can silence the linter. You cannot silence the platform. Most "I disabled the check but it still fails" confusion comes from mixing these up.
The Three Severity Levels
Every check carries a severity, and you can reassign them in config:
| Severity | Integer | Meaning |
|---|---|---|
| error | 0 | Likely broken behavior — fix before shipping |
| warning | 1 | Risky pattern or performance hit |
| info | 2 | Style and best-practice suggestions |
By default a run fails (non-zero exit code) when at least one error-severity offense exists. The --fail-level flag changes that threshold, which becomes important in CI.
Installing and Running Your First Check
If you have the Shopify CLI installed, you already have Theme Check. Confirm with:
shopify theme check --versionNo install? Get the CLI through npm:
npm install -g @shopify/cli@latestYour First Run
From your theme's root directory (the folder containing layout/, sections/, templates/):
shopify theme checkYou'll get a list of offenses grouped by file, with the check name in brackets. A fresh Dawn fork typically reports a handful; an older agency theme can report hundreds. Don't panic — most of the noise disappears with ten lines of config, covered below.
Useful Flags to Know on Day One
The full flag list lives in Shopify's theme check CLI reference, but these four do most of the work:
shopify theme check --list # list every enabled check
shopify theme check --print # output the active config to stdout
shopify theme check -o json # machine-readable output
shopify theme check --fail-level error # only exit non-zero on errors--listtells you which checks your config actually enables--printshows the merged config Theme Check resolved — invaluable when an ignore pattern "isn't working"-o jsonfeeds scripts and CI annotations-a/--auto-correctfixes auto-correctable offenses in place
The --path Argument: Syntax That Actually Works

This is the part of Shopify theme check that trips up the most developers, so let's be precise. Per the CLI reference, --path is "the path where you want to run the command" — it points at your theme root directory, not at an individual file.
Checking a Theme From Another Directory
The supported use case: you're outside the theme folder, or your theme builds into a subdirectory.
# Check the theme in the current directory (default behavior)
shopify theme check --path .
# Check a theme that lives somewhere else
shopify theme check --path ./themes/storefront
# Monorepo or build output: check the compiled theme
shopify theme check --path ./distYou can also set it via environment variable, which is handy in CI scripts:
SHOPIFY_FLAG_PATH=./dist shopify theme checkWhy --path path/to/file.liquid Doesn't Do What You Expect
Developers constantly reach for this:
# This is NOT a supported way to lint one file
shopify theme check --path sections/header.liquidThe current CLI treats --path as the theme directory to analyze. Single-file and glob targeting was requested by the community in the theme-tools repo, but the CLI's model is whole-theme analysis — checks like UnusedSnippet and TranslationKeyExists require seeing every file to work at all, so a single-file run would produce false positives by design.
How to Check Just One File Anyway
Three honest workarounds, in order of practicality:
- Filter the JSON output. Run the whole theme, slice out the file you care about:
shopify theme check -o json | jq '[.[] | select(.path | endswith("sections/header.liquid"))]'- Use the VS Code extension's single-file mode. Set
themeCheck.onlySingleFileCheckstotrueand the language server lints only open files, skipping whole-theme checks (details in the VS Code section below). - Scope a custom config. Create a second config that ignores everything except the directory you're iterating on, and load it with
-C:
shopify theme check -C .theme-check-sections.ymlFor a tighter feedback loop than any of these, pair the CLI with an AI coding agent — our walkthrough on Claude Code and Shopify CLI integration shows how to have an agent run the check, parse the JSON, and fix offenses file by file.
Configuring .theme-check.yml the Right Way

The .theme-check.yml file in your theme root is where Theme Check goes from noisy to useful. Generate a starter file with:
shopify theme check --initThe full option reference is in Shopify's Theme Check configuration docs, but here's the working knowledge.
Ignore Patterns That Actually Match
The ignore property takes glob patterns relative to the theme root:
ignore:
- 'node_modules/**'
- 'assets/vendor-*.js'
- 'snippets/icon-*.liquid'
- 'templates/customers/**'Two gotchas worth bolding: patterns must quote-match the path from the theme root, and `--print` is your debugger — if an ignore "isn't working," print the merged config and confirm your pattern survived the merge.
Enabling, Disabling, and Re-Severitizing Checks
Each check is a top-level YAML key. You can toggle it, change its severity, ignore it for specific paths, or set check-specific options:
# Start from Shopify's recommended baseline
extends: theme-check:recommended
# Kill a check entirely
UnusedAssign:
enabled: false
# Keep a check but demote it from error to warning
TemplateLength:
severity: warning
max_length: 600
# Keep a check everywhere except legacy files
ParserBlockingJavaScript:
ignore:
- snippets/legacy-tracking.liquidThe extends key accepts theme-check:recommended, theme-check:all, and theme-check:theme-app-extension — when multiple configs are extended, objects deep-merge and the last entry wins.
A Sane Starter Config for an Inherited Theme
Inherited a ten-year-old theme with 400 offenses? Don't disable everything. Ratchet instead:
extends: theme-check:recommended
ignore:
- 'assets/vendor/**'
TemplateLength:
enabled: false
UndefinedObject:
severity: warning| Do this | Not this |
|---|---|
| Demote noisy checks to warning, fix errors first | Set enabled: false on everything that fires |
| Ignore vendor/generated files by glob | Ignore entire sections/ or snippets/ dirs |
| Commit .theme-check.yml to the repo | Keep personal configs out of version control |
| Re-enable one check per sprint and fix it | Declare lint bankruptcy forever |
Inline Disables and Auto-Correct
Sometimes one line legitimately violates a rule — a parser-blocking script a third-party app demands, a snippet that looks unused but is rendered dynamically. Disable the check at the call site instead of globally:
{% # theme-check-disable ParserBlockingJavaScript %}
<script src="{{ 'legacy-widget.js' | asset_url }}"></script>
{% # theme-check-enable ParserBlockingJavaScript %}Scoping the Disable
Three scopes are supported:
- Specific check, bounded region — the example above; this should be your default
- Multiple checks — comma-separate them:
{% # theme-check-disable UnusedAssign,SpaceInsideBraces %} - Whole file — put
{% # theme-check-disable %}on the first line of the file
When Inline Disables Are the Wrong Tool
A disable comment is a documented exception. If you're pasting the same disable into fifteen files, that's a config rule, not an exception — move it to .theme-check.yml. And if you're disabling LiquidHTMLSyntaxError or other parse-level checks, stop: you're hiding a real defect that platform validation will reject at upload anyway. Keep a copy of our Shopify Liquid cheat sheet for developers nearby and fix the syntax instead.
Auto-Correct: What It Fixes and What It Won't
Instead of disabling, let Theme Check repair mechanical offenses — missing defer attributes, whitespace inside braces, simple deprecated-filter swaps — with the auto-correct flag. Run it on a clean git working tree so the diff shows exactly what changed:
git status # confirm clean tree first
shopify theme check -a
git diff # review every correctionIt will not restructure logic, split oversized templates, resolve undefined objects, or fix translation keys — anything requiring intent stays on your plate. Treat -a like a code formatter, not a code reviewer, and never run it for the first time five minutes before a deploy.
Running Shopify Theme Check in CI

A linter that only runs on your machine protects exactly one machine. Putting Shopify theme check in CI makes it a team contract.
GitHub Actions With theme-check-action
Shopify maintains shopify/theme-check-action, which wraps the CLI and annotates pull requests with inline offense comments:
# .github/workflows/theme-check.yml
name: Theme Check
on: [push]
jobs:
theme-check:
name: Theme Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Theme Check
uses: shopify/theme-check-action@v2
with:
theme_root: '.'
token: ${{ github.token }}The action accepts a flags input for passing CLI flags (its README uses --fail-level as the example) and a version input to pin a specific @shopify/theme release — pinning matters, as the regressions section below explains.
Choosing a Fail Level
--fail-level sets the minimum severity that produces a non-zero exit code. The pragmatic CI strategy:
- Start at `--fail-level error` — block merges only on likely-broken code
- Burn down warnings over a few sprints using the ratchet config approach
- Tighten the level once the baseline is clean
Failing CI on style suggestions from day one guarantees developers will route around the check.
The Last Line of Defense: theme push --strict
Even without CI, you can make deploys self-checking:
shopify theme push --strictThe --strict flag requires Theme Check to pass without errors before pushing — warnings are allowed through. Set SHOPIFY_FLAG_STRICT_PUSH=1 in your deploy environment to enforce it without anyone remembering the flag. For more workflow patterns like this, browse our Shopify development articles.
VS Code Integration: The Shopify Liquid Extension
The same engine behind the CLI powers the official Shopify Liquid extension — install it from the VS Code Marketplace. You get syntax highlighting, completions, Liquid Prettier formatting, and live Theme Check diagnostics as squiggles in the editor.
Setup and Requirements
The extension runs the Theme Check language server, so it expects the Shopify CLI on your machine. It reads the same .theme-check.yml from your theme root, which means editor diagnostics, CLI runs, and CI all agree — one config, three surfaces. If your editor shows offenses the CLI doesn't (or vice versa), run shopify theme check --print and confirm which config each is resolving.
Settings Worth Changing
The defaults check aggressively. Tune them in settings.json:
{
"themeCheck.checkOnOpen": true,
"themeCheck.checkOnChange": false,
"themeCheck.checkOnSave": true,
"themeCheck.onlySingleFileChecks": true
}checkOnChange: falsestops the linter re-running on every keystroke in large themesonlySingleFileChecks: trueskips whole-theme checks (UnusedSnippet,TranslationKeyExists) while you're iterating on one file — the closest thing to "check a single file" that exists today
When the Extension Goes Quiet
If diagnostics stop appearing: confirm the CLI is on your PATH, check the extension's output channel for language-server crashes, and make sure you opened the theme root as the workspace folder — opening a parent monorepo folder can keep the server from finding the theme.
Common Theme Check Errors and How to Fix Them

These are the offenses that dominate real-world runs, with the fix that clears each one.
Performance Checks
| Check | What it means | Fix |
|---|---|---|
| ParserBlockingJavaScript | <script> without defer/async blocks rendering | Add defer (or async for independent scripts) |
| ImgWidthAndHeight | <img> missing width/height causes layout shift | Add explicit width and height attributes |
| RemoteAsset | Asset served from a third-party domain | Move the file to assets/ and use asset_url |
| AssetSizeJavaScript | JS bundle exceeds the size threshold | Split bundles; lazy-load below-the-fold scripts |
Liquid Correctness Checks
`UndefinedObject` fires when you reference a variable that doesn't exist in that template's scope — frequently a snippet expecting a variable the parent forgot to pass. Fix it by passing the variable explicitly via {% render 'snippet', product: product %}.
`UnusedAssign` flags {% assign %} variables never read; delete them. `UnusedSnippet` flags snippets never rendered — but beware dynamic renders with variable names, which the static analyzer can't follow. That's a legitimate inline-disable case.
`LiquidHTMLSyntaxError` means the parser couldn't build a tree at all. Fix these first: every other check in that file is unreliable until the file parses.
Translation and JSON Checks
`TranslationKeyExists` fires when {{ 'products.product.add_to_cart' | t }} references a key missing from locales/en.default.json. Add the key to the default locale. JSON syntax errors in template or config files are flagged too — and these are worth treating as sacred, because malformed JSON templates get rejected server-side no matter what your linter config says. Theme design choices interact with several of these checks; our theme design archive digs into the performance side.
What You Can and Can't Bypass During shopify theme dev
Here's the mental model that saves hours of confused debugging.
What You CAN Bypass
Everything that belongs to the linter:
- Check offenses —
theme devdoes not run Theme Check as a gate; errors and warnings inshopify theme checkoutput won't stop your dev server - Editor noise — config ignores, severity demotions, and inline disables all silence diagnostics without touching dev or push
- Push gating —
theme pushonly consults Theme Check when you pass--strict
What You CANNOT Bypass
Two layers sit beneath your config and don't read .theme-check.yml:
- Server-side validation. Files with broken Liquid syntax or malformed JSON get rejected at upload. The dev server logs the upload error and your local change simply never reaches the preview. No config silences this — the file must parse.
- The CLI's internal file processing.
shopify theme devparses theme files itself to sync them, and that parsing is not governed by your ignore list. This is exactly what bit users in Shopify CLI issue #5918:theme devhung for minutes loggingLiquidHTMLParsingErrors on themes full of generated snippets (Replo chunks, Zipify pages) — even after adding those files to the `.theme-check.yml` ignore list. The same theme pushed fine, becausetheme pushskipped that processing path.
Practical Escape Hatches
When the un-bypassable layer misbehaves on generated or third-party files:
- Use
shopify theme push+ a preview theme as a temporary substitute fortheme dev - Pin the CLI to the last version that behaved (
#5918reproduced on 3.80.7 but not 3.80.0) - Quarantine generated files outside the theme root and copy them in at build time
For more debugging patterns like this, our troubleshooting archive is worth a bookmark.
CLI Version Regressions: When Theme Check Feels Broken but Isn't
Theme Check's reputation suffers for sins committed by the CLI around it. Two documented cases prove the pattern.
The theme dev Hang (#5918)
Covered above: a point release (3.80.0 → 3.80.7) introduced file-checking behavior that hung theme dev for 3+ minutes on snippet-heavy themes before erroring out. Nothing in the theme was newly wrong — the CLI changed underneath it.
The Slow Push Era (#4210)
Shopify CLI issue #4210 documents theme push taking 15 seconds to 2 minutes on themes that uploaded in under 5 seconds on CLI 3.47.x — a regression that coincided with the new colorful upload UI and persisted across versions 3.56–3.64. If your "Theme Check workflow" suddenly feels slow, time the push and the check separately before blaming the linter.
The Defensive Posture
- Pin your CLI version in
package.jsonrather than installing@latestglobally, and pin theversioninput intheme-check-action - Upgrade deliberately — bump in a branch, run
theme devand a full check, then roll forward - Bisect by version when behavior changes:
npm install -g @shopify/cli@3.80.0style downgrades take one minute and instantly tell you whether the problem is your code or the tool - Search the issue tracker first — third-party writeups like Scale Shopify's Theme Check setup guide are useful for setup, but live regressions surface on GitHub days before anywhere else
Make the Linter Part of the Team
Shopify Theme Check pays for its setup cost the first time it catches a missing translation key or a parser-blocking script before a customer does. The playbook from this guide, compressed:
- Run
shopify theme check --init, extendtheme-check:recommended, and commit the config - Point
--pathat the theme root — filter JSON output or use the VS Code single-file mode when you need one file - Ratchet severity in
.theme-check.ymlinstead of declaring lint bankruptcy - Gate merges with
theme-check-actionat--fail-level error, and deploys withpush --strict - Remember the two layers you can't bypass: server-side validation and the CLI's own file processing
- Pin CLI versions and bisect before assuming your theme is at fault
Theme tooling changes fast — version regressions, new checks, and CLI rewrites all land between doc updates. The fastest way to stay current is to ask people who hit the bug yesterday: the Talk Shop Shopify developer community on Discord is full of theme devs comparing configs, CLI versions, and workarounds in real time. Bring your gnarliest .theme-check.yml and join us — what's the one check you've rage-disabled, and was it ever actually wrong?

About Talk Shop
The Talk Shop team — insights from our community of Shopify developers, merchants, and experts.
