React Ecommerce Deployment: Deploy to Production
Deploying a React ecommerce store to production is your final step—and the most critical. Deployment isn't just uploading files; it's configuring servers, enabling HTTPS, caching assets on CDNs, monitoring performance, and preparing for traffic spikes on Black Friday. This article shows you how to deploy a production-grade ecommerce store that handles thousands of concurrent users, loads in under 2 seconds, and stays online during traffic surges. You'll learn deployment strategies used by Shopify, Vercel, and other platforms handling billions of ecommerce transactions annually.
Optimize Your Build for Production
First, ensure your React build is optimized. In your vite.config.js:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
build: {
target: 'esnext', // Use modern JavaScript
minify: 'terser', // Aggressive minification
sourcemap: false, // Disable source maps in production
rollupOptions: {
output: {
manualChunks: {
'vendor': ['react', 'react-dom', 'react-router-dom'],
'stripe': ['@stripe/react-stripe-js', '@stripe/js'],
'state': ['zustand'],
},
},
},
},
define: {
'process.env.NODE_ENV': JSON.stringify('production'),
},
});
This configuration:
- Chunks dependencies so updates don't invalidate all caches
- Disables source maps to reduce bundle size
- Minifies aggressively using Terser
Run npm run build and check your bundle size:
npm run build
# vite v5.0.0 building for production...
# ✓ 1234 modules transformed.
# dist/index.html 0.45 kB │ gzip: 0.30 kB
# dist/assets/index-a1b2c3d4.js 142.33 kB │ gzip: 45.12 kB
# dist/assets/vendor-e5f6g7h8.js 89.45 kB │ gzip: 29.34 kB
# dist/assets/stripe-i9j0k1l2.js 22.56 kB │ gzip: 7.89 kB
Aim for total gzipped size under 100 KB for fast page load. Use code splitting to lazy-load routes:
import { lazy, Suspense } from 'react';
const Products = lazy(() => import('./pages/Products'));
const Checkout = lazy(() => import('./pages/Checkout'));
const OrderHistory = lazy(() => import('./pages/OrderHistory'));
export default function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Products />} />
<Route path="/checkout" element={<Checkout />} />
<Route path="/orders" element={<OrderHistory />} />
</Routes>
</Suspense>
);
}
Now only the product listing loads on page 1; checkout code loads only when needed.
Deploy to Vercel (Recommended for React)
Vercel is the easiest deployment for React apps. It handles caching, CDN, auto-scaling, and SSL automatically.
- Create a Vercel account at vercel.com.
- Link your GitHub repo:
npm i -g vercel
vercel - Configure environment variables in your Vercel Dashboard:
VITE_API_URL=https://api.yourdomain.comVITE_STRIPE_PUBLIC_KEY=pk_live_...
- Deploy: Push to
mainbranch and Vercel auto-deploys.
Vercel's built-in features:
- CDN: Static assets cached globally, served from 280+ edge locations (Cloudflare).
- Edge Functions: Serverless functions for API calls, running near users.
- Analytics: Web Vitals monitoring, performance insights.
- Rollback: One-click rollback to previous deployments.
Alternative: Deploy to AWS or Self-Hosted
For more control, deploy to AWS S3 + CloudFront:
# Build your app
npm run build
# Upload to S3
aws s3 sync dist/ s3://your-bucket-name --delete
# Invalidate CloudFront cache
aws cloudfront create-invalidation --distribution-id YOUR_DIST_ID --paths "/*"
Or use Docker + Kubernetes for complete control. Create a Dockerfile:
# Build stage
FROM node:18 AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
# Production stage
FROM node:18
RUN npm install -g serve
COPY --from=builder /app/dist /app
EXPOSE 3000
CMD ["serve", "-s", "/app", "-l", "3000"]
Build and push to a container registry:
docker build -t yourdomain/ecommerce:latest .
docker push yourdomain/ecommerce:latest
# Deploy to Kubernetes
kubectl apply -f deployment.yaml
Enable HTTPS and Security Headers
HTTPS is mandatory for ecommerce (required by Stripe). If self-hosting, use Let's Encrypt:
# Using Certbot
sudo certbot certonly --standalone -d yourdomain.com
Add security headers to your server:
// Express.js backend
const helmet = require('helmet');
app.use(helmet());
// Adds:
// Content-Security-Policy: block XSS, clickjacking
// X-Frame-Options: deny (prevent framing)
// Strict-Transport-Security: force HTTPS
Configure CDN Caching
Serve static assets from a CDN (Cloudflare, AWS CloudFront) with long cache headers:
// In your web server (Nginx, Express)
app.use(express.static('dist', {
maxAge: '1y', // Cache for 1 year
etag: false, // Disable etag for static assets
}));
// But don't cache HTML (let browser check for updates)
app.get('/', (req, res) => {
res.set('Cache-Control', 'no-cache, no-store, must-revalidate');
res.sendFile('dist/index.html');
});
This strategy:
- JavaScript/CSS hashed filenames (e.g.,
index-a1b2c3d4.js) cached for 1 year index.htmlnever cached; browser checks for updates daily- When you deploy, old assets remain cached; new code refs new filenames
Monitor Performance and Errors
Use Sentry for error tracking:
npm install @sentry/react @sentry/tracing
In your main.jsx:
import * as Sentry from '@sentry/react';
Sentry.init({
dsn: import.meta.env.VITE_SENTRY_DSN,
environment: import.meta.env.MODE,
tracesSampleRate: 0.1, // 10% of requests
});
const App = () => {
// Your app code
};
export default Sentry.withProfiler(App);
Now errors are logged to Sentry Dashboard with stack traces, user sessions, and performance data.
Test Before Going Live
Perform a pre-launch checklist:
- Lighthouse Audit: Run
npm run build && npx lighthouse https://staging.yourdomain.com. Target scores: Performance 90+, Accessibility 90+, Best Practices 90+. - Security Headers: Use securityheaders.com to verify HTTPS, CSP, X-Frame-Options.
- Load Testing: Use
k6orJMeterto simulate 1,000 concurrent users. - Payment Testing: Process test payments with Stripe test keys.
- Mobile Testing: Test on iPhone and Android with slow 3G to catch performance issues.
Example k6 load test:
// load-test.js
import http from 'k6/http';
import { check } from 'k6';
export let options = {
stages: [
{ duration: '2m', target: 100 },
{ duration: '5m', target: 100 },
{ duration: '2m', target: 200 },
{ duration: '5m', target: 200 },
{ duration: '2m', target: 0 },
],
};
export default function () {
let res = http.get('https://yourdomain.com');
check(res, {
'status is 200': (r) => r.status === 200,
'page load under 2s': (r) => r.timings.duration < 2000,
});
}
Run: k6 run load-test.js.
Scaling for Traffic Spikes
Black Friday can bring 10x normal traffic. Prepare:
- Auto-scaling: Enable auto-scaling on your database and API servers.
- Read Replicas: If your API queries are heavy, replicate the database for read-heavy operations.
- Cache Layer: Redis caches frequent queries (product listings, filtering).
// Cache product listings for 1 hour
const redis = require('redis');
const client = redis.createClient();
app.get('/api/products', async (req, res) => {
const key = `products:${JSON.stringify(req.query)}`;
// Check cache
const cached = await client.get(key);
if (cached) return res.json(JSON.parse(cached));
// Fetch from DB
const products = await db.query('SELECT * FROM products WHERE ...');
// Cache for 1 hour
await client.setex(key, 3600, JSON.stringify(products));
res.json(products);
});
Key Takeaways
- Minimize bundle size with code splitting and lazy loading; target total gzip under 100 KB.
- Use Vercel for ease, or AWS/Kubernetes for control.
- Enable HTTPS and security headers; use helmet.js.
- Cache static assets on CDN for 1 year; never cache HTML.
- Monitor with Sentry; load test before launch.
- Auto-scale for peak traffic; use Redis to cache database queries.
Frequently Asked Questions
How much does it cost to deploy a store?
Vercel: Free tier, then $20/month for pro features. AWS: $10–50/month depending on traffic. Self-hosted: $5–20/month for VPS. Stripe: 2.9% + $0.30 per transaction.
How do I deploy database migrations safely?
Migrations should be backwards-compatible. Deploy the new backend code, run migrations, then deploy new frontend. Never deploy frontend before migrations are done.
What if something breaks in production?
Vercel + GitHub allow one-click rollback to previous deployment. For self-hosted, use blue-green deployment: keep two versions running, switch traffic instantly on failures.
How do I test payment processing in production?
Use Stripe's test keys in a staging environment first. For production, use Stripe's webhook testing tool to simulate transactions. Never test with real cards.