build-site.mjs) that wraps Vite’s production bundler to create dual SPA entry points for flexible routing.
Build Architecture
The build process produces a dual-entry SPA structure:dist/index.html— Root entry point served at/dist/app/index.html— App entry point served at/appand all SPA routesdist/app/assets/— Bundled JS, CSS, fonts, and images
Build Command
Build Pipeline
The build process follows these sequential steps:Clean Output Directory
Remove the entire This happens in
dist/ directory to ensure no stale artifacts remain.scripts/build-site.mjs:33Vite Production Build
Run This compiles:
npm run build:app which invokes vite build with production optimizations.- TypeScript → JavaScript (ES2020)
- React JSX → optimized JS
- Tailwind CSS → minified styles
- Asset imports → content-hashed filenames
dist/app/index.html + dist/app/assets/*Mirror SPA Shell at Root
Copy This ensures first-time visitors hitting
dist/app/index.html to dist/index.html so / and /app share the same auth-first entry point./ immediately see the authentication UI instead of a 404.Vite Configuration
The build is controlled byvite.config.ts which handles environment-aware asset paths and code splitting.
Base Path Strategy
- Development (
npm run dev): Base path is/so local routes like/adminand/profileresolve correctly - Production (
npm run build): Base path is/app/because Vercel serves the SPA from/app/index.htmlvia rewrites
Code Splitting
Vite automatically splits vendor dependencies into separate chunks for optimal caching:- Core framework code (React) loads first
- Heavy animation library (Framer Motion) is isolated
- Icon library doesn’t pollute main bundle
- Browser can cache vendor chunks independently
Output Structure
Afternpm run build completes, the dist/ directory contains:
Content hashes in filenames (e.g.,
index-a3b2c1d4.js) enable aggressive long-term caching — browsers only re-download files when content changes.Production Optimizations
Vite applies these optimizations automatically duringnpm run build:
- Minification: JS via esbuild, CSS via Lightning CSS
- Tree shaking: Dead code elimination
- Asset inlining: Small images/fonts become base64 data URIs
- CSS extraction: Styles extracted into separate
.cssfiles - Compression hints: Brotli/gzip-friendly output structure
Vercel Build Configuration
Thevercel.json file controls how Vercel builds and deploys the app:
The
ignoreCommand prevents Vercel from building non-production branches — only staging and main trigger deployments.Troubleshooting
Build fails with TypeScript errors
Run type checking before building:tsc --noEmit to surface type errors without attempting a full build.
Assets fail to load in production
Verify the Vitebase path matches your deployment routing:
- Local dev should use
base: '/' - Production should use
base: '/app/'to match Vercel’s SPA rewrites
Build produces stale artifacts
Run a clean build to remove cached output:“Module not found” errors after build
Check theresolve.alias configuration in vite.config.ts — the @ alias must point to src/ for imports like @/lib/supabase.
Build Performance
Typical build times:- Clean build: 8-12 seconds
- Incremental build: 3-5 seconds (Vite cache enabled)
- Vercel production build: 15-25 seconds (includes dependency installation)
Vercel caches
node_modules between deployments, significantly reducing build time for commits that don’t change dependencies.