Development Environment Setup Intermediate 12 min read

Shopify CLI Integration and Hot Reload Workflow

Connect your React build process with Shopify CLI for a seamless development experience. Run both tools together with the --path flag pointing to your shopify-theme folder.

Now that we have Vite (or Webpack) building our React code directly to shopify-theme/assets/, we need to connect it with Shopify CLI. The key is telling CLI to only sync the shopify-theme/ folder—not our entire project.

The Development Flow

Here’s what we want to achieve:

┌──────────────────────────────────────────────────────────────┐
│ YOUR LOCAL MACHINE │
│ │
│ ┌─────────────┐ ┌──────────────────────────────────┐ │
│ │ Edit React │ │ Vite │ │
│ │ src/*.tsx │ ────▶ │ Watches src/ │ │
│ └─────────────┘ │ Outputs to shopify-theme/assets │ │
│ └──────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ ┌──────────────────────────────────┐ │
│ │ Edit Liquid │ │ Shopify CLI │ │
│ │ shopify- │ ────▶ │ Watches shopify-theme/ │ │
│ │ theme/* │ │ Syncs ONLY that folder │ │
│ └─────────────┘ └──────────────────────────────────┘ │
│ │ │
└────────────────────────────────────────│─────────────────────┘
┌───────────────────────────────┐
│ Development Store │
│ yourstore.myshopify.com │
└───────────────────────────────┘
┌───────────────────────────────┐
│ Browser Auto-Refresh │
│ See changes instantly │
└───────────────────────────────┘

Prerequisites

Install Shopify CLI if you haven’t:

Terminal window
# macOS/Linux
brew tap shopify/shopify
brew install shopify-cli
# Or with npm (all platforms)
npm install -g @shopify/cli @shopify/theme

Verify installation:

Terminal window
shopify version
# Should output: 3.x.x or higher

Step 1: Authenticate with Shopify

Log in to your Shopify Partner or store account:

Terminal window
shopify auth login

This opens a browser for authentication. Once complete, you’re ready to connect to your development store.

Step 2: The Key Flag: --path

Since our Shopify theme lives in a subfolder, we must tell CLI where it is:

Terminal window
# Point CLI to the shopify-theme folder
shopify theme dev --path shopify-theme --store your-store.myshopify.com

This is crucial. Without --path, CLI would try to sync your entire project (including node_modules, src/, etc.) which would fail.

Step 3: Running Both Tools Together

You need two processes running simultaneously:

  1. Vite watching src/ → building directly to shopify-theme/assets/
  2. Shopify CLI watching shopify-theme/ → syncing to your store

Option A: Two Terminal Windows

The simplest approach—open two terminals:

Terminal 1: Vite

Terminal window
npm run dev

Terminal 2: Shopify CLI

Terminal window
shopify theme dev --path shopify-theme --store your-store.myshopify.com

Use concurrently to run both in a single terminal:

Terminal window
npm install -D concurrently

Update package.json:

{
"scripts": {
"dev": "vite build --watch --mode development",
"theme": "shopify theme dev --path shopify-theme --store your-store.myshopify.com",
"start": "concurrently \"npm:dev\" \"npm:theme\"",
"build": "tsc --noEmit && vite build",
"push": "npm run build && shopify theme push --path shopify-theme"
}
}

What each script does:

  • dev: Vite watches src/ and rebuilds to shopify-theme/assets/ on changes
  • theme: Shopify CLI watches shopify-theme/ and syncs to your dev store
  • start: Runs both together—this is your main development command
  • build: Typecheck first, then build optimized production bundle
  • push: Build then deploy to Shopify

Now run everything with:

Terminal window
npm start

Output shows both processes:

[dev] vite v5.0.0 building for development...
[theme] ✓ Theme dev server running at http://127.0.0.1:9292
[dev] ✓ built in 234ms
[dev] shopify-theme/assets/react-bundle.js 45.12 kB
[theme] Syncing theme #123456789...
[dev] watching for file changes...

Option C: npm-run-all (Alternative)

Another popular option:

Terminal window
npm install -D npm-run-all
{
"scripts": {
"dev": "vite build --watch",
"theme": "shopify theme dev --path shopify-theme",
"start": "run-p dev theme"
}
}

Step 4: Configure Shopify CLI

Create a shopify.theme.toml configuration file in your project root:

shopify.theme.toml
[environments.development]
store = "your-store.myshopify.com"
path = "shopify-theme"
# theme = "123456789" # Optional: pin to specific theme ID
[environments.staging]
store = "your-store.myshopify.com"
path = "shopify-theme"
theme = "987654321"
[environments.production]
store = "your-store.myshopify.com"
path = "shopify-theme"
theme = "111222333"

Now you can run without flags:

Terminal window
# Uses development environment by default
shopify theme dev
# Or specify environment
shopify theme dev --environment staging

Update your npm scripts to use environments:

{
"scripts": {
"theme": "shopify theme dev",
"theme:staging": "shopify theme dev --environment staging",
"start": "concurrently \"npm:dev\" \"npm:theme\""
}
}

Step 5: No Ignore List Needed!

Because we’re only syncing shopify-theme/, we don’t need complex ignore patterns. The folder contains only deployable files:

shopify-theme/
├── assets/ # ✓ Synced (includes React bundle)
├── config/ # ✓ Synced
├── layout/ # ✓ Synced
├── locales/ # ✓ Synced
├── sections/ # ✓ Synced
├── snippets/ # ✓ Synced
└── templates/ # ✓ Synced
# These are OUTSIDE shopify-theme/ and never synced:
src/ # React source
node_modules/ # Dependencies

If you do need ignores (e.g., for local testing files), add them to the config:

[environments.development]
path = "shopify-theme"
ignore = [
"assets/*.map", # Skip source maps
"**/.DS_Store" # Skip macOS files
]

Step 6: Environment-Specific Configuration

Create environment files for different contexts:

.env.development
VITE_DEBUG=true
VITE_API_URL=https://your-store.myshopify.com
# .env.production
VITE_DEBUG=false
VITE_API_URL=https://your-store.myshopify.com

Access in React:

const isDebug = import.meta.env.VITE_DEBUG === 'true';
const apiUrl = import.meta.env.VITE_API_URL;

Step 7: Browser Sync and Auto-Refresh

Shopify CLI provides automatic browser refresh when files change. The flow:

  1. Edit a React file → Vite rebuilds to shopify-theme/assets/
  2. Shopify CLI detects the change → Syncs to store → Triggers browser refresh

Hot Reloading Caveat

Unlike a standard React dev server, we can’t do true HMR (Hot Module Replacement) because:

  • React runs inside Shopify’s hosted page
  • Shopify CLI does full page refreshes

The refresh is still fast (1-2 seconds), but React state resets on each change. Mitigate this by:

  1. Using URL-based state where possible
  2. Persisting dev state to localStorage
  3. Using React DevTools for state inspection
/*
* Persist state across page refreshes during development
* Since Shopify CLI does full page refreshes (not HMR), state resets on every change.
* This middleware saves state to localStorage so it survives refreshes.
*/
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
const useCart = create(
persist(
(set) => ({
items: [],
// ... actions
}),
{
name: 'cart-storage', // localStorage key
// State is automatically saved on changes and restored on page load
}
)
);

Step 8: Debugging Tips

View Console Logs

Open browser DevTools to see your React logs:

src/main.tsx
if (import.meta.env.DEV) {
console.log('🚀 React bundle loaded');
console.log('Environment:', import.meta.env.MODE);
}

Inspect Network Requests

Watch the Network tab for:

  • react-bundle.js loading successfully
  • API calls to /cart/add.js, /cart.js, etc.
  • Any 404 errors for missing assets

React DevTools

Install the React DevTools browser extension:

This lets you inspect component trees and state even on your Shopify dev store.

Source Maps

Ensure source maps are enabled for debugging:

vite.config.ts
export default defineConfig({
build: {
sourcemap: true,
},
});

In DevTools, you’ll see your original .tsx files instead of bundled code.

Common Workflow Issues

”Path not found” or theme not syncing

Verify the path is correct:

Terminal window
# Check the folder exists
ls shopify-theme/
# Test CLI can find it
shopify theme list --path shopify-theme

“Store not found”

Ensure you’re authenticated and have access:

Terminal window
shopify auth login
shopify theme list --store your-store.myshopify.com

Bundle not updating in browser

  1. Verify Vite is running and watching
  2. Check Shopify CLI is syncing (look for “Syncing…” messages)
  3. Hard refresh: Cmd+Shift+R (Mac) or Ctrl+Shift+R (Windows)

“Port already in use”

If Shopify CLI can’t start:

Terminal window
# Find what's using the port
lsof -i :9292
# Use a different port
shopify theme dev --path shopify-theme --port 9293

TypeScript errors not showing

Run typecheck separately:

Terminal window
npm run typecheck

Or add it to your start script:

{
"scripts": {
"start": "concurrently \"npm:dev\" \"npm:theme\" \"npm:typecheck:watch\"",
"typecheck:watch": "tsc --noEmit --watch"
}
}

Complete Development Workflow

Here’s the full workflow you’ll use daily:

Terminal window
# 1. Start development (Vite + Shopify CLI)
npm start
# 2. Open the preview URL (shown in terminal)
# Usually http://127.0.0.1:9292
# 3. Edit files:
# - React: src/components/*.tsx → Vite builds → Syncs automatically
# - Liquid: shopify-theme/sections/*.liquid → Syncs directly
# 4. View changes in browser (auto-refreshes)
# 5. When done, Ctrl+C to stop both processes

Production Deployment

For production, build once and push:

Terminal window
# Build production bundle (minified, no console.log)
npm run build
# Push entire theme to production
shopify theme push --path shopify-theme --environment production

Or use the combined script:

Terminal window
npm run push # Runs build then push

Complete Package.json

Here’s the complete package.json with all scripts:

{
"name": "shopify-react-theme",
"private": true,
"type": "module",
"scripts": {
"dev": "vite build --watch --mode development",
"build": "tsc --noEmit && vite build",
"theme": "shopify theme dev --path shopify-theme",
"theme:staging": "shopify theme dev --path shopify-theme --environment staging",
"start": "concurrently \"npm:dev\" \"npm:theme\"",
"typecheck": "tsc --noEmit",
"typecheck:watch": "tsc --noEmit --watch",
"push": "npm run build && shopify theme push --path shopify-theme",
"push:staging": "npm run build && shopify theme push --path shopify-theme --environment staging",
"push:production": "npm run build && shopify theme push --path shopify-theme --environment production"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"zustand": "^4.4.0"
},
"devDependencies": {
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.0",
"@vitejs/plugin-react": "^4.2.0",
"concurrently": "^8.2.0",
"typescript": "^5.3.0",
"vite": "^5.0.0"
}
}

Key Takeaways

  1. Use --path shopify-theme to tell CLI where your theme folder is
  2. Run Vite and CLI together with concurrently for single-command startup
  3. No complex ignore lists needed—the folder structure handles separation
  4. Configure environments in shopify.theme.toml for dev/staging/production
  5. npm start is your single command for the complete dev workflow

Your development environment is now complete! In the next module, we’ll build the bridge that passes data from Liquid to React.

Finished this lesson?

Mark it complete to track your progress.

Discussion

Loading comments...