Build Configuration & SPA Routing
Build Arguments
VITE_API_BASE_URL
Pass the API endpoint at build time:
docker build \
--build-arg VITE_API_BASE_URL=https://api.staging.com \
-t stockease-frontend:staging .
docker build \
--build-arg VITE_API_BASE_URL=https://api.production.com \
-t stockease-frontend:production .In Dockerfile:
ARG VITE_API_BASE_URL=""
ENV VITE_API_BASE_URL=${VITE_API_BASE_URL}
RUN npm run buildIn Code (Vite replaces at build time):
const apiUrl = import.meta.env.VITE_API_BASE_URL;
// Becomes:
const apiUrl = "https://api.production.com";.dockerignore
Excludes files from Docker build context, improving build speed.
# Version Control
.git
.gitignore
.github
# Dependencies (reinstalled)
node_modules
.pnp
# Build Artifacts
dist
coverage
typedoc-output
architecture-html
# Tests (not needed)
__tests__
*.test.ts
*.test.tsx
*.test.js
vitest.config.ts
jest.config.js
# Documentation
docs
README.md
CHANGELOG.md
*.md
# IDE & Tools
.vscode
.idea
.DS_Store
*.swp
*.swo
Thumbs.db
# Development Files
.env.local
.env.development
.env.test
Performance Impact
With .dockerignore:
Build context: ~50MB
Build time: 10-15s
Without .dockerignore:
Build context: 200MB+
Build time: 30-40s
SPA Routing Configuration
Problem
Regular web servers return 404 for non-root routes:
GET /admin β 404 (file not found)
GET /user β 404 (file not found)
But these routes should serve index.html so
React Router can handle them client-side.
Solution: nginx Configuration
location ~ ^/assets/ {
# Static assets: serve with long cache headers
expires 1y;
add_header Cache-Control "public, immutable";
try_files $uri =404;
}
location / {
# Everything else: serve index.html for SPA routing
try_files $uri /index.html;
}
Request Flow
GET /assets/index.js
β
Match: location ~ ^/assets/
β
Serve: /usr/share/nginx/html/assets/index.js
β
Cache-Control: 1 year
GET /admin
β
No match: doesn't start with /assets/
β
Fall through to: location /
β
try_files $uri /index.html
β
Serve: /usr/share/nginx/html/index.html
β
React Router: renders /admin page
Complete Configuration Example
server {
listen 80;
root /usr/share/nginx/html;
index index.html;
# Gzip compression
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# Static assets with long caching
location ~ \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# API requests: pass through
location /api/ {
proxy_pass $VITE_API_BASE_URL;
}
# SPA routing: serve index.html
location / {
try_files $uri /index.html =404;
}
# Health check
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
}
Development vs Production
Development (Vite)
npm run dev
# Vite server running on http://localhost:5173
# Hot reload on file changes
# Full source maps
# No optimizationsProduction (Docker)
docker build -t app:latest .
docker run -p 80:80 app:latest
# nginx serving static files
# Optimized bundle
# Long-lived cache
# No source mapsBuild Optimization Strategies
Layer Caching
Order Dockerfile to cache dependencies before source:
# This layer caches if package*.json unchanged
COPY package*.json ./
RUN npm ci
# This layer rebuilds if src/ changed (common case)
COPY src ./src
RUN npm run buildBenefit: Only rebuild app code when necessary, reuse npm dependencies.
BuildKit
Enable Docker BuildKit for faster builds:
DOCKER_BUILDKIT=1 docker build -t app:latest .Benefits:
- Parallel layer building
- Better caching
- Smaller context uploads
- More efficient builds
Verification
Check Build Arguments Were Used
# Build with API URL
docker build --build-arg VITE_API_BASE_URL=https://api.example.com -t app .
# Check nginx config inside container
docker run app cat /etc/nginx/conf.d/default.conf
# Verify SPA routing works
docker run -p 8080:80 app &
sleep 2
curl http://localhost:8080/admin/ # Should return index.html contentRelated Documentation
Last Updated: November 2025