Add Frame to Vite.
Frame expects a Shopify theme at the Vite root and source files in
src. JavaScript works in place of TypeScript.
Requires Node.js 22.12 or newer and Vite 7 or 8.
pnpm add -D vite-plugin-shopify-frame vite
import {defineConfig} from 'vite';
import frame from 'vite-plugin-shopify-frame';
export default defineConfig({
plugins: [frame()],
});
Add these scripts to your
package.json:
{
"scripts": {
"dev": "vite",
"build": "vite build"
}
}
{% render 'frame-assets' %}
.frame/
assets/frame-*
snippets/frame-assets.liquid
Zero configuration entry
Frame creates a bundle named
theme
from
src/main.ts
or
src/main.js, plus
src/style.css.
Two tools, one workflow.
Run Vite and Shopify CLI independently. Vite serves frontend assets while Shopify CLI synchronizes the theme.
npm run dev
shopify theme dev \
--notify .frame/shopify-ready
The notification lets Frame request a full page reload after Shopify CLI finishes an update. It is optional. Vite HMR and Shopify CLI live reload continue to work without it.
Build, then deploy normally.
Frame publishes generated assets into the theme and replaces the development loader with Shopify CDN asset tags.
npm run build
shopify theme push
Run the build before
shopify theme push
or
shopify theme package. Shopify CLI remains responsible for deployment.
Load code where it belongs.
Start with the default bundle. Add named bundles when a substantial feature should only load on selected Liquid surfaces.
frame({
bundles: {
theme: {script: 'main.ts', style: 'style.css'},
product: {script: 'product.ts', style: 'product.css'},
},
});
{% render 'frame-assets', entry: 'theme' %}
{% if request.page_type == 'product' %}
{% render 'frame-assets', entry: 'product' %}
{% endif %}
-
Use dynamic imports first
They keep one entry architecture while Vite loads optional features on demand.
-
Use bundles for clear boundaries
Product configurators, account areas, and store locators are good candidates.
Small API, explicit control.
Every Frame option is optional.
frame({
// Theme directory relative to the Vite root.
theme: '.',
// Source directory relative to the Vite root.
source: 'src',
// Namespace for generated assets and Liquid.
namespace: 'frame',
// Set to false to disable coordinated reloads.
refresh: {
signal: '.frame/shopify-ready',
delay: 100,
},
});
Bring the Vite ecosystem.
Frame does not wrap frontend frameworks. Add their official Vite plugins as usual.
-
Tailwind CSS
Official Vite plugin
-
Alpine.js
Standard entry import
-
React
Fast Refresh ready
-
Vue
Single file components
-
Sass
Native Vite support
-
PostCSS
Native Vite config
React Fast Refresh requires
@vitejs/plugin-react/preamble
because Frame does not use an HTML entry. Framework code mounted inside
sections should handle Shopify Theme Editor section load and unload events.
Built for a shared assets directory.
Shopify mixes generated assets and hand authored files in one directory. Frame cleans up without treating that directory like a disposable build folder.
-
Ownership ledger
Only files recorded as Frame output can be removed as stale.
-
Isolated staging
Every build finishes away from the theme before publication.
-
Protected writes
Unknown theme files are never silently overwritten.
-
Recovery journal
Interrupted publication is restored before the next build continues.
See it in a real theme.
Every example is independently runnable, checked in CI, and tested against a Shopify development store.
-
Vanilla
TypeScript, CSS, dynamic imports, fonts, and images
-
Alpine and Tailwind
Alpine.js with Tailwind CSS through Vite
-
React
React components mounted inside Liquid sections
-
Vue
Vue single file components inside Liquid sections