Skip to main content

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.

Vercel is the easiest deployment for React apps. It handles caching, CDN, auto-scaling, and SSL automatically.

  1. Create a Vercel account at vercel.com.
  2. Link your GitHub repo:
    npm i -g vercel
    vercel
  3. Configure environment variables in your Vercel Dashboard:
    • VITE_API_URL=https://api.yourdomain.com
    • VITE_STRIPE_PUBLIC_KEY=pk_live_...
  4. Deploy: Push to main branch 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.html never 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 k6 or JMeter to 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.

Further Reading​