Development
Start the dev servers and understand how changes flow from your editor to the browser.
Start the dev servers
pnpm dev -- --store my-store.myshopify.comThis runs two servers in parallel:
| Server | What it does | URL |
|---|---|---|
shopify theme dev | Proxies your store, serves Liquid-rendered pages | Printed in terminal (~http://127.0.0.1:9292) |
vite | Serves JS/CSS with hot module replacement | http://localhost:5173 |
Open the Shopify CLI URL in your browser. You don't need to open the Vite URL — the theme loads assets from it automatically.
TIP
With STORE set in .env, you can run pnpm dev without the --store flag. Any flags after -- pass through to shopify theme dev.
How the dual-server setup works
The vite-tag snippet bridges Shopify and Vite. In theme/layout/theme.liquid:
{%- liquid
render 'importmap'
render 'vite-tag', entry: 'theme.css', preload_stylesheet: true
render 'vite-tag', entry: 'theme.js'
-%}In development, vite-tag points <script> and <link> tags to localhost:5173 for HMR. In production, it points to Shopify CDN URLs from the Vite build manifest.
Don't edit vite-tag.liquid or importmap.liquid — they're auto-generated by vite-plugin-shopify.
What gets hot-reloaded
| File type | Behavior |
|---|---|
CSS (theme/frontend/styles/) | Instant style injection, no page reload |
JS islands (theme/frontend/islands/) | HMR via Vite, component re-imports without full reload |
JS libraries (theme/frontend/lib/) | HMR where supported, otherwise full reload |
| Liquid files | Full page reload after Shopify CLI syncs (~2-3 seconds) |
| Theme settings JSON | Full page reload after Shopify CLI syncs |
Editing workflow
Liquid — Edit files in theme/. Shopify CLI watches for changes, syncs them to the store, and triggers a browser reload.
CSS — The main entry point is theme/frontend/entrypoints/theme.css. Tailwind v4 is processed by the @tailwindcss/vite plugin (no tailwind.config.js). Add utility classes in Liquid templates or custom styles in theme/frontend/styles/.
Islands — Each island is a file in theme/frontend/islands/. The hydration runtime discovers custom elements in the DOM and loads matching islands automatically. Add a new file, and import.meta.glob() picks it up.
Shared JS — Utility modules live in theme/frontend/lib/. Import with the @/ alias:
import { debounce } from '@/lib/utils'Tips
Port conflicts — If port 5173 is taken, Vite picks the next available port automatically. The vite-tag snippet reads the port from Vite's dev server, so this works transparently.
Multiple stores — Pass a different --store flag to work against other stores:
pnpm dev -- --store staging-store.myshopify.comTheme editor — The Shopify theme editor works alongside shopify theme dev. Edits in the editor sync back to your local files, but can overwrite local changes to JSON template/section files if you edit them simultaneously.
Lint and format — Run before committing:
pnpm lint # ESLint
pnpm format # Prettier (Liquid, JS, CSS)Next steps
- Deployment — Build and ship to production
- Islands Architecture — How the hydration system works
- Creating Islands — Write your first Web Component