React.js Fundamentals

Deploying React Applications

15 min Lesson 37 of 40

Production Build Process

Before deploying your React application, you need to create an optimized production build. This process minifies code, removes development warnings, and optimizes performance.

Creating a Production Build

# For Create React App npm run build # For Vite npm run build # For Next.js npm run build npm start # Start production server # Build output build/ ├── static/ │ ├── css/ │ │ └── main.abc123.css │ ├── js/ │ │ ├── main.xyz789.js │ │ └── vendor.def456.js │ └── media/ ├── index.html └── asset-manifest.json
Build Optimizations:
  • Minification: Code is compressed, whitespace removed
  • Tree Shaking: Unused code is eliminated
  • Code Splitting: JS split into smaller chunks
  • Asset Hashing: Files get unique hashes for cache busting
  • Environment Variables: Production values are injected

Environment Variables

Different environments need different configuration. React apps handle environment variables securely:

# .env.development REACT_APP_API_URL=http://localhost:3000/api REACT_APP_ENVIRONMENT=development REACT_APP_ANALYTICS_ID= # .env.production REACT_APP_API_URL=https://api.myapp.com REACT_APP_ENVIRONMENT=production REACT_APP_ANALYTICS_ID=GA-XXXXX-Y # Access in code const apiUrl = process.env.REACT_APP_API_URL; const isDev = process.env.NODE_ENV === 'development'; if (process.env.REACT_APP_ANALYTICS_ID) { initAnalytics(process.env.REACT_APP_ANALYTICS_ID); }
Security Warning: Never commit sensitive keys to version control!
  • Create .env.local for secrets (auto-ignored by CRA)
  • Use REACT_APP_ prefix for public variables only
  • Store API keys, tokens on backend, not in React app
  • Use CI/CD environment variables for deployment secrets

Deploying to Vercel

Vercel offers the easiest deployment experience for React apps, especially Next.js:

# Install Vercel CLI npm install -g vercel # Login vercel login # Deploy from project directory vercel # Deploy to production vercel --prod # vercel.json configuration { "buildCommand": "npm run build", "outputDirectory": "build", "framework": "create-react-app", "env": { "REACT_APP_API_URL": "@api-url" }, "regions": ["iad1"], "github": { "enabled": true, "autoAlias": true } }
Vercel Features:
  • Automatic Deployments: Push to Git, auto-deploy
  • Preview URLs: Every PR gets a unique preview URL
  • Edge Network: CDN with 100+ global locations
  • Zero Config: Detects framework automatically
  • Serverless Functions: Add API routes easily

Deploying to Netlify

Netlify is another excellent platform for static sites and React apps:

# Install Netlify CLI npm install -g netlify-cli # Login netlify login # Initialize project netlify init # Deploy netlify deploy # Deploy to production netlify deploy --prod # netlify.toml configuration [build] command = "npm run build" publish = "build" [build.environment] NODE_VERSION = "18" REACT_APP_API_URL = "https://api.myapp.com" [[redirects]] from = "/*" to = "/index.html" status = 200 [[headers]] for = "/static/*" [headers.values] Cache-Control = "public, max-age=31536000, immutable"
SPA Routing Fix: The redirect rule /* → /index.html is crucial for client-side routing! Without it, refreshing /about returns 404. This tells the server to always serve index.html, letting React Router handle navigation.

Deploying with Docker

Docker containers provide consistent environments across development and production:

# Dockerfile FROM node:18-alpine AS build WORKDIR /app # Copy package files COPY package*.json ./ RUN npm ci # Copy source and build COPY . . RUN npm run build # Production stage with nginx FROM nginx:alpine # Copy build files COPY --from=build /app/build /usr/share/nginx/html # Copy nginx config COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]
# nginx.conf server { listen 80; server_name _; root /usr/share/nginx/html; index index.html; # Enable gzip compression gzip on; gzip_types text/css application/javascript application/json image/svg+xml; gzip_min_length 1000; # Caching for static assets location /static/ { expires 1y; add_header Cache-Control "public, immutable"; } # SPA fallback location / { try_files $uri $uri/ /index.html; } }
# Build and run Docker container docker build -t my-react-app . docker run -p 80:80 my-react-app # docker-compose.yml version: '3.8' services: web: build: . ports: - "80:80" environment: - NODE_ENV=production restart: unless-stopped

CI/CD Pipeline with GitHub Actions

Automate testing and deployment with continuous integration:

# .github/workflows/deploy.yml name: Deploy to Production on: push: branches: [main] jobs: build-and-deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup Node.js uses: actions/setup-node@v3 with: node-version: '18' cache: 'npm' - name: Install dependencies run: npm ci - name: Run tests run: npm test -- --coverage --watchAll=false - name: Build run: npm run build env: REACT_APP_API_URL: ${{ secrets.API_URL }} REACT_APP_ANALYTICS_ID: ${{ secrets.ANALYTICS_ID }} - name: Deploy to Vercel uses: amondnet/vercel-action@v20 with: vercel-token: ${{ secrets.VERCEL_TOKEN }} vercel-org-id: ${{ secrets.ORG_ID }} vercel-project-id: ${{ secrets.PROJECT_ID }} vercel-args: '--prod'
CI/CD Best Practices:
  • Run Tests: Prevent broken code from deploying
  • Lint Code: Enforce code quality standards
  • Security Scan: Check for vulnerabilities (npm audit)
  • Preview Deployments: Deploy PRs to staging URLs
  • Rollback Plan: Keep ability to revert to previous version

Performance Optimization for Production

Additional optimizations to implement before deployment:

// 1. Code Splitting with React.lazy import React, { lazy, Suspense } from 'react'; const Dashboard = lazy(() => import('./Dashboard')); const Profile = lazy(() => import('./Profile')); function App() { return ( <Suspense fallback={<LoadingSpinner />}> <Routes> <Route path="/dashboard" element={<Dashboard />} /> <Route path="/profile" element={<Profile />} /> </Routes> </Suspense> ); } // 2. Image Optimization import { LazyLoadImage } from 'react-lazy-load-image-component'; function ProductImage({ src, alt }) { return ( <LazyLoadImage src={src} alt={alt} effect="blur" width={400} height={300} /> ); } // 3. Service Worker for Offline Support if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker .register('/service-worker.js') .then(registration => { console.log('SW registered:', registration); }) .catch(error => { console.log('SW registration failed:', error); }); }); }

Monitoring and Analytics

// Setup error tracking with Sentry import * as Sentry from "@sentry/react"; Sentry.init({ dsn: process.env.REACT_APP_SENTRY_DSN, environment: process.env.REACT_APP_ENVIRONMENT, tracesSampleRate: 1.0, }); // Setup Google Analytics import ReactGA from 'react-ga4'; ReactGA.initialize(process.env.REACT_APP_GA_ID); function App() { const location = useLocation(); useEffect(() => { ReactGA.send({ hitType: "pageview", page: location.pathname }); }, [location]); return <Routes>...</Routes>; } // Performance monitoring import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals'; function sendToAnalytics({ name, value, id }) { ReactGA.event({ category: 'Web Vitals', action: name, value: Math.round(value), label: id, }); } getCLS(sendToAnalytics); getFID(sendToAnalytics); getFCP(sendToAnalytics); getLCP(sendToAnalytics); getTTFB(sendToAnalytics);
Exercise 1: Deploy your React app to Vercel:
  • Create a production build locally and test it
  • Set up environment variables for different environments
  • Connect your GitHub repository to Vercel
  • Configure automatic deployments on push to main
  • Set up preview deployments for pull requests
Exercise 2: Create a complete CI/CD pipeline:
  • Set up GitHub Actions workflow
  • Add steps for: install, lint, test, build
  • Configure deployment to your hosting platform
  • Add status badges to your README
  • Test the pipeline by pushing changes
Exercise 3: Optimize your production build:
  • Implement code splitting for at least 3 routes
  • Add lazy loading for images
  • Set up performance monitoring (Web Vitals)
  • Configure caching headers for static assets
  • Run Lighthouse audit and achieve 90+ scores