Development
Starting the dev servers
pnpm dev -- --store my-store.myshopify.comThis command uses concurrently to run two servers in parallel:
| Server | What it does | URL |
|---|---|---|
shopify theme dev --path theme | Proxies your Shopify store, serves Liquid-rendered pages | Printed in terminal (typically http://127.0.0.1:9292) |
vite | Serves frontend assets (JS, CSS) with hot module replacement | http://localhost:5173 |
Open the Shopify CLI URL in your browser. You do not need to open the Vite URL directly -- the theme loads assets from it automatically.
TIP
If you have STORE set in your .env file, the Shopify CLI will use it automatically and you can run pnpm dev without the --store flag. Any additional flags after -- are passed through to shopify theme dev.
How the dual-server setup works
The bridge between Shopify and Vite is the vite-tag snippet. In theme/layout/theme.liquid, the layout renders two entry points:
{%- liquid
render 'importmap'
render 'vite-tag', entry: 'theme.css', preload_stylesheet: true
render 'vite-tag', entry: 'theme.js'
-%}The vite-tag snippet is auto-generated by vite-plugin-shopify and switches behavior based on environment:
- Development -- Points
<script>and<link>tags tolocalhost:5173, enabling Vite's HMR client. CSS and JS changes appear instantly without a full page reload. - Production -- Points to Shopify CDN asset URLs using the Vite build manifest, serving minified and optimized bundles.
WARNING
Do not edit theme/snippets/vite-tag.liquid or theme/snippets/importmap.liquid manually. They are regenerated by the Vite plugins on every build.
What gets hot-reloaded
| File type | Behavior |
|---|---|
CSS (theme/frontend/styles/, entrypoints) | Instant style injection, no page reload |
JavaScript islands (theme/frontend/islands/) | HMR via Vite -- component re-imports without full reload |
JavaScript libraries (theme/frontend/lib/) | HMR where supported, otherwise full reload |
| Liquid templates | Full page reload triggered by Shopify CLI file sync |
| Theme settings JSON | Full page reload triggered by Shopify CLI file sync |
CSS changes are the fastest feedback loop. Liquid changes require the Shopify CLI to sync the file to the store, which takes a few seconds before the page reloads.
Editing workflow
Liquid (sections, blocks, snippets, layouts, templates)
Edit Liquid files in theme/. The Shopify CLI watches for file changes and syncs them to your development store. The browser reloads automatically when the sync completes.
CSS
The main stylesheet entry point is theme/frontend/entrypoints/theme.css, which imports the design token layer (theme/frontend/styles/theme.css) and component styles. Tailwind CSS v4 is processed by the @tailwindcss/vite plugin -- there is no tailwind.config.js file.
Add utility classes directly in Liquid templates. For custom styles, add them to the appropriate CSS layer file in theme/frontend/styles/.
JavaScript islands
Each island is a file in theme/frontend/islands/ that defines a Web Component. The hydration runtime (revive.js) automatically discovers custom elements in the DOM and loads matching island files based on hydration directives:
<!-- In a Liquid snippet -->
<quantity-input client:visible>
<!-- server-rendered content -->
</quantity-input>When you edit an island file, Vite's HMR re-imports the module. Create new islands by adding a file to theme/frontend/islands/ -- the hydration runtime picks them up automatically via import.meta.glob().
Shared JavaScript
Utility modules live in theme/frontend/lib/. Import them into islands using the @/ path alias:
import { debounce } from '@/lib/utils'Both @/ and ~/ resolve to theme/frontend/.
Common dev tips
Port conflicts -- If port 5173 is in use, Vite automatically picks the next available port and updates the HMR client URL. The vite-tag snippet reads the port from Vite's dev server, so this works transparently.
Multiple stores -- Pass different --store flags to work against different development stores. Each store has its own theme data, so you can test against different configurations:
pnpm dev -- --store staging-store.myshopify.comTheme editor -- The Shopify theme editor works alongside shopify theme dev. Changes made in the editor are synced back to your local files. Be aware that this can overwrite local edits to JSON template/section files if you edit them simultaneously.
Linting and formatting -- Run these before committing:
pnpm lint # ESLint
pnpm format # Prettier (formats Liquid, JS, and CSS)What's next
- Deployment -- Building and shipping to production
- Islands Architecture -- How the hydration system works in detail
- Creating Islands -- Guide to writing new Web Components