Build pipeline
How a TypeScript file in src/ becomes a script tag on the storefront.
The two build steps
npm run build runs two commands in order.
npm run build:css runs PostCSS over src/assets/theme_in.css, which contains only the three Tailwind directives, and
writes the result to src/entrypoints/theme.styles.css. Tailwind scans ./**/*.liquid and ./src/**/*.{js,ts,jsx,tsx}
for class names, so a class used only in Liquid still ends up in the CSS.
vite build then bundles everything in src/entrypoints, including the theme.styles.css that PostCSS just wrote. It
writes files with content hashes in their names to assets/, plus a manifest at assets/vite-manifest.json.
Running vite build on its own skips the Tailwind step, and the CSS will lack any utility class added since the last
CSS build. Use npm run build.
Why the vite-tag snippet exists
Liquid cannot read the Vite manifest, so it cannot look up that mini-cart.ts compiled to mini-cart-B0CCwZRp.js.
vite-plugin-shopify generates snippets/vite-tag.liquid to fill that gap. The snippet is a long chain of conditionals
that maps each source path to its hashed asset and module preloads.
A Liquid file loads a bundle like this:
{%- render 'vite-tag' with 'mini-cart/elements/mini-cart.ts' -%}The path is relative to src/entrypoints. If it matches no branch in the generated snippet, the render outputs nothing
and no error appears anywhere. When a script is missing from the page, check this path first. See
Asset not loading.
Generated files that are also committed
Git tracks two build outputs.
snippets/vite-tag.liquid is committed because the theme on Shopify needs it, and Shopify only receives files pushed to
the theme.
src/entrypoints/theme.styles.css is committed because Vite reads it as input. Without it, a fresh clone cannot build
the theme styles.
The next build overwrites a hand edit to either file. Change the sources instead. For styles, edit
src/assets/theme_in.css and the Tailwind config. For scripts, edit the entrypoint.
What stays out of the theme
.shopifyignore keeps development files out of the theme upload. It excludes src/, scripts/, node_modules/, the
Vite and Tailwind configs, package.json and the agent instruction files. Shopify receives the compiled output in
assets/ plus the Liquid, config and locale files.
A developer who opens the theme in Shopify's code editor sees the compiled bundles and cannot see or edit the TypeScript.
Asset cleanup
vite.config.js adds a plugin called shopifyAssetCleanup. assets/ holds theme files alongside build output, and
emptyOutDir is off. Without the plugin, old hashed files would pile up on every build and get pushed to Shopify.
At the start of a build, the plugin reads the previous manifest and deletes the files it lists. After the build, it removes any leftover file whose name matches the hashed pattern of a bundle the build still produces. It never touches files outside that pattern, so images and hand-written assets are safe.
Development mode
npm run dev starts four processes through run-pty:
| Pane | Command | Purpose |
|---|---|---|
| vite | vite | Dev server on port 5173 over HTTPS, with hot module replacement |
| css | postcss --watch | Rebuilds theme.styles.css when Tailwind classes change |
| shopify | shopify theme dev | Serves the theme on port 9292 against the dev environment |
| tunnel | node scripts/start-cloudflare-tunnel.mjs | Exposes the local preview on a public URL |
In development, vite-tag points at the Vite dev server instead of hashed assets, so JavaScript and CSS changes appear
without a theme push. The Shopify CLI syncs Liquid changes.
The Vite server uses HTTPS with a local certificate that vite-plugin-mkcert creates. On a fresh clone, the first run
may ask for your password to install that certificate.
Starting the dev server rewrites snippets/vite-tag.liquid to point at localhost:5173 and the Vite client. The
production theme depends on the same file, so run npm run build before you commit or push. After a dev session, check
git status. If snippets/vite-tag.liquid is modified and you did not mean to rebuild, it holds dev server URLs that
would break the storefront.
Source map
| Concern | File |
|---|---|
| Vite configuration, entrypoint discovery, asset cleanup | vite.config.js |
Tailwind content globs, brand colours, tw- prefix | tailwind.config.cjs |
| PostCSS plugin chain | postcss.config.cjs |
| Build and dev scripts | package.json |
| Dev process layout | run-pty.json |
TypeScript compiler options and @/ path alias | tsconfig.json |
| Files excluded from the theme upload | .shopifyignore |
Related
- Entrypoints maps every bundle to the Liquid that loads it.
- Local development covers running the build day to day.
- Asset not loading covers a script that does not appear.