Half the Shopify CLI questions I get are the same one: why does shopify theme dev keep opening a browser login in a pipeline that has no browser? The answer is a single environment variable, and it is not the one you would guess from the flag name. This is the copy-paste reference I keep open, weighted toward the part every other cheat sheet skips: running the theme commands non-interactively.
TL;DR: A working Shopify CLI reference for theme developers. It leads with the thing zero-click searchers keep hunting for, running theme commands non-interactively in CI with SHOPIFY_CLI_THEME_TOKEN and SHOPIFY_FLAG_STORE, then covers theme dev, check, console, push and the full lifecycle. Every flag here is checked against the current CLI, not a 2023 tutorial.
Download the CLI cheat sheet (PDF)
shopify theme dev, and every flag worth knowing
shopify theme dev starts a local preview server with hot reload against a store. On its first run it opens a browser login unless a token is already in the environment (more on that next). The everyday shape:
shopify theme dev --store my-store
shopify theme dev --store my-store --theme 123456789012 # attach to a specific remote theme
shopify theme dev --live-reload hot-reload # hot-reload (default) | full-page | off
shopify theme dev --host 0.0.0.0 --port 9293 # rebind for Docker/WSL
shopify theme dev --theme-editor-sync # pull admin editor changes back to local
Two things save real pain here. theme dev creates a temporary development theme that does not count against your theme limit, but it is deleted after 7 days of inactivity and immediately when you run shopify auth logout, so never rely on that preview URL surviving a session. And -a/--allow-live exists to run dev against the live theme: it is blocked by default for a reason, and you do not point it at a client’s live store. The full flag set and the rest of the theme dev workflow sit in Shopify’s command reference.
How do you run shopify theme dev non-interactive (headless CI)?
This is the section everything else is downstream of. To skip the browser login, set two environment variables and let the CLI read them:
export SHOPIFY_CLI_THEME_TOKEN=shptka_xxxxxxxx # env var for --password
export SHOPIFY_FLAG_STORE=my-store.myshopify.com # env var for --store
shopify theme dev
The trap that eats hours: the env var for --password is literally SHOPIFY_CLI_THEME_TOKEN, not SHOPIFY_FLAG_PASSWORD. Every other flag follows the SHOPIFY_FLAG_* pattern (SHOPIFY_FLAG_STORE, SHOPIFY_FLAG_PATH, SHOPIFY_FLAG_THEME_ID), and --password is the one irregular exception. Get the name wrong and the command silently falls back to an interactive login and hangs your job.
One more rule that bites: precedence is flag beats env var beats shopify.theme.toml. A stray --store on the command line will quietly override your CI env var. If a step still hangs on a confirmation, add SHOPIFY_FLAG_FORCE=1. And note Shopify’s own CI/CD guide authenticates the same way but runs theme check and theme push, not theme dev.
What is SHOPIFY_CLI_THEME_TOKEN, and how do you get a Theme Access token?
The token is a Theme Access password (or an Admin API token). You generate a Theme Access password from the free Theme Access app on the store. The emailed link expires after 7 days or the moment it is viewed once, so paste the password straight into your CI secrets.
shopify theme push --password shptka_xxxxxxxx --store my-store.myshopify.com # terminal; use the env var in CI
The scope detail decides whether your script works. A Theme Access password is scoped to write_themes only, which is enough for push, pull, dev and console. A script that also touches products, metafields or other Admin resources needs a custom app access token with those specific scopes instead. That custom-app path is also where unauthenticated_read_content comes in: a themes app needs read_themes and write_themes, plus unauthenticated_read_content (Storefront API) to enable hot reloading. There are three auth methods in total: a Shopify account login, a Theme Access password, and a custom app token.
shopify.theme.toml: named environments, and –password vs –store-password
Stop retyping store, theme and password on every command by putting them in shopify.theme.toml:
[environments.staging]
theme = "123456789012"
store = "my-store"
password = "shptka_123456"
shopify theme dev --environment staging # or: -e staging
[environments.default] applies automatically without the flag; any other named block needs -e. And the two password flags people constantly mix up are genuinely different: --password is the CLI-to-store auth token (Theme Access or Admin API), while --store-password is the storefront password-page password for a locked store. They solve different problems. Environments work across check, dev, console, delete, info, list, publish, pull, push, rename and share, and the full environments reference documents which keys get ignored inside a block.
shopify theme check: –path vs positional arguments, and CI gating
This resolves a query I see constantly. On the current CLI, --path is the only way to scope a run. There are no positional arguments, so shopify theme check ./sections/header.liquid is invalid; you cannot lint a single file.
shopify theme check --path ./my-theme # scope to a directory (defaults to cwd)
shopify theme check -a # auto-correct fixable offenses in place
shopify theme check --fail-level warning # exit 1 on warnings (default is error only)
shopify theme check -o json > results.json # machine-readable, flat array per file
shopify theme check --list # every active check + severity
Two current gotchas. The default --fail-level is error, so a CI job that only checks the exit code passes while warnings quietly pile up: pass --fail-level warning to block on them. And --category and --exclude-category were removed in Theme Check 2.x (January 2024), so older posts that show them are wrong now; use -C theme-check:all to run everything, per the theme check reference. This is the linter that keeps a Liquid codebase honest before it ships, and it validates the section schema settings too.
shopify theme console: the offline Liquid REPL
Almost no cheat sheet mentions this one. shopify theme console starts a Liquid REPL: an interactive terminal for evaluating Liquid objects, filters and tags against real store data, with no browser and no running dev server.
shopify theme console
shopify theme console --url /products/classic-leather-jacket # load real page context
shopify theme console -s my-store.myshopify.com
The one thing that trips people up: without --url, context objects like product, collection and cart return nil, because the REPL has no page context by default. Pass a real relative path and they resolve to live data, which makes it perfect for checking a tricky filter chain or loop before you wire it into a custom section. It has eight flags, no --theme and no --port (those belong to theme dev), and its --password env var is the same SHOPIFY_CLI_THEME_TOKEN.
theme push, pull and publish: the deploy lifecycle
The commands that move code, with the guardrails that stop a bad day:
shopify theme push --unpublished --json # new unpublished theme, ids/urls as JSON
shopify theme push --theme 123456789012 --strict # block push unless theme check passes
shopify theme push --theme 123456789012 --only sections/hero.liquid # push named files only
shopify theme pull --live --nodelete # pull live, keep extra local files
shopify theme publish --theme 123456789012 --force # promote an already-pushed theme to live
Three traps worth internalising. --nodelete means opposite things by direction: on push it protects remote files, on pull it protects local files. theme publish cannot publish local code, it only promotes a theme you already pushed, so the sequence is always push then publish by id. And --strict blocks only on Theme Check errors, so a push can succeed with warnings still open. Worth knowing for 2026: theme init now clones Shopify’s Skeleton theme by default, not Dawn, which matters when you are choosing a base for a new build or a performance rebuild.
Shopify CLI auth and the global env vars
The session and config commands people still get wrong, because the old names were retired:
shopify auth login # not "shopify login"
shopify auth logout # warning: deletes any active dev theme
shopify organization list # replaces the old "shopify whoami"
shopify version
SHOPIFY_CLI_NO_ANALYTICS=1 shopify theme dev --store my-store # opt out of telemetry
There is no bare shopify login, shopify logout or shopify whoami on the current CLI. Since CLI 4.0 (May 2026) the tool self-upgrades through your package manager by default and skips upgrading inside CI, and it now needs Node 22.12+ and Git 2.28+. SHOPIFY_CLI_NO_ANALYTICS=1 only silences telemetry, it does not suppress prompts, which is SHOPIFY_FLAG_FORCE=1’s job. The complete general-commands list has the rest.
Why theme dev is not your CI command
If you take one habit from this page, take this one. theme dev opens a long-running preview server with hot reload; that is a development tool, not a pipeline step, and dropping it into CI is why so many jobs hang. Shopify’s documented pattern is a linter gate plus a push:
shopify theme check --fail-level error # gate: fail the build on errors
shopify theme push --json --theme staging --store $SHOPIFY_FLAG_STORE --password $SHOPIFY_CLI_THEME_TOKEN
For preview-per-PR, shopify theme push --development-context "pr-482" ties a development theme to a stable id like a PR number or branch, which is the idiomatic pattern. Only reach for theme dev in a pipeline for a genuine headless visual-regression run that needs a live URL, and even then remember the dev theme evaporates on logout. Keep the copy-paste reference next to your editor with the PDF above, and pair it with the Liquid cheat sheet for the templating side.
The takeaway
- Set
SHOPIFY_CLI_THEME_TOKENandSHOPIFY_FLAG_STOREto run any theme command non-interactively, and never reach forSHOPIFY_FLAG_PASSWORD, which does not exist. - Generate the token from the free Theme Access app (
write_themesscope) and save it to CI secrets before the one-time link expires. - Scope
theme checkwith--pathonly, and pass--fail-level warningso CI actually blocks on lint warnings. - Use
theme console --url /products/<handle>to test Liquid against real data from the terminal, no browser needed. - Keep
theme devout of your pipeline: gate withtheme check, deploy withtheme push, and download the PDF so every flag is one glance away.
Kaspian Fuad is a Shopify developer and CRO consultant who builds and ships themes through CI for DTC and B2B brands. 12 years in ecommerce, 100+ stores, Top Rated Plus on Upwork. Book a free 30-minute call if you want your theme build and deploy pipeline set up so nothing hangs and nothing touches the live theme by accident.