How to Deploy a React App to Production in 2025

· 20 min read

Deploying a React app to production doesn't have to be complicated. This comprehensive guide walks you through four different deployment strategies—from the simplest (Vercel/Netlify) to the most flexible (DigitalOcean/AWS)—so you can choose the best option for your project.

🎁 Get $500 in Free Hosting Credits:

Quick Comparison: Which Deployment Strategy is Right for You?

PlatformBest ForMonthly CostSetup TimeDifficulty
VercelHobby projects, small teamsFree - $205 minutesEasy ⭐
NetlifyStatic sites, JAMstack appsFree - $195 minutesEasy ⭐
DigitalOceanProduction apps, full control$6 - $2415-30 minutesMedium ⭐⭐
AWS S3 + CloudFrontHigh-traffic apps, enterprise$1 - $50+30-60 minutesHard ⭐⭐⭐

Table of Contents

Method 1: Deploy to Vercel (Easiest)

Vercel is the fastest way to deploy a React app. It's built by the creators of Next.js, but works perfectly with Create React App, Vite, and other React setups.

Prerequisites

Step 1: Prepare Your React App

Ensure your package.json has the correct build script:

{
  "scripts": {
    "build": "vite build",    // for Vite
    // OR
    "build": "react-scripts build"  // for Create React App
  }
}

Step 2: Deploy to Vercel

  1. Go to vercel.com and sign up
  2. Click "Add New Project"
  3. Import your Git repository
  4. Vercel auto-detects React settings:
    • Framework Preset: Create React App / Vite
    • Build Command: npm run build
    • Output Directory: dist (Vite) or build (CRA)
  5. Click "Deploy"

Your app will be live in ~2 minutes at your-app.vercel.app.

Step 3: Add Custom Domain (Optional)

  1. Go to Project Settings → Domains
  2. Add your domain (e.g., myapp.com)
  3. Update DNS records as instructed
  4. Vercel automatically provisions SSL certificate

Environment Variables

Add environment variables in Project Settings → Environment Variables:

VITE_API_URL=https://api.example.com
VITE_STRIPE_KEY=pk_live_xxxxx

Note: Vite uses VITE_ prefix, Create React App uses REACT_APP_ prefix.

Pricing

✅ When to Use Vercel:

  • You want the fastest deployment (5 minutes)
  • Your app is a hobby project or MVP
  • You value simplicity over cost optimization
  • You're using Next.js (Vercel's sweet spot)

Method 2: Deploy to Netlify

Netlify is similar to Vercel but offers better pricing for high-bandwidth sites and includes features like form handling and identity management.

Step 1: Deploy via Git

  1. Sign up at netlify.com
  2. Click "Add new site" → "Import an existing project"
  3. Connect your Git provider (GitHub, GitLab, Bitbucket)
  4. Select your React repository
  5. Configure build settings:
    • Build command: npm run build
    • Publish directory: dist or build
  6. Click "Deploy site"

Step 2: Configure Redirects (SPA Routing)

For React Router to work correctly, create a public/_redirects file:

/*    /index.html   200

This ensures all routes redirect to index.html (client-side routing).

Step 3: Environment Variables

Go to Site Settings → Environment Variables and add:

VITE_API_URL=https://api.example.com
VITE_ANALYTICS_ID=G-XXXXXXXXXX

Netlify CLI (Alternative Method)

# Install Netlify CLI
npm install -g netlify-cli

# Build your app
npm run build

# Deploy
netlify deploy --prod --dir=dist

Pricing

✅ When to Use Netlify:

  • You need more bandwidth than Vercel's free tier
  • You want built-in form handling
  • You're building a JAMstack app (static + APIs)
  • You value free tier generosity

Method 3: Deploy to DigitalOcean (Best Value)

DigitalOcean App Platform provides a middle ground: easier than manual server setup, cheaper than Vercel/Netlify for production apps.

💰 DigitalOcean Bonus:

Get $200 in free credits (valid for 60 days) — perfect for testing production deployments.

Option A: App Platform (Easiest on DigitalOcean)

  1. Go to DigitalOcean App Platform
  2. Click "Create App"
  3. Connect your GitHub/GitLab repository
  4. Select your React repo and branch
  5. DigitalOcean auto-detects settings:
    • Type: Static Site
    • Build Command: npm run build
    • Output Directory: dist
  6. Choose plan: Starter ($0) or Basic ($5/mo)
  7. Click "Launch App"

Deployment completes in 3-5 minutes. Your app gets a free .ondigitalocean.app domain.

Option B: Droplet + Nginx (Full Control)

For maximum flexibility, deploy to a Droplet (virtual machine):

Step 1: Create Droplet

# Choose:
# - Ubuntu 24.04 LTS
# - Basic plan: $6/mo (1GB RAM)
# - Datacenter: Closest to your users

Step 2: SSH into Droplet

ssh root@your-droplet-ip

Step 3: Install Nginx

sudo apt update
sudo apt install nginx -y
sudo systemctl start nginx
sudo systemctl enable nginx

Step 4: Deploy React Build

On your local machine, build your app:

npm run build

Upload to Droplet:

scp -r dist/* root@your-droplet-ip:/var/www/html/

Step 5: Configure Nginx for React Router

Edit /etc/nginx/sites-available/default:

server {
    listen 80;
    server_name your-domain.com;
    root /var/www/html;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;
    }

    # Gzip compression
    gzip on;
    gzip_types text/plain text/css application/json application/javascript;
}

Restart Nginx:

sudo nginx -t
sudo systemctl restart nginx

Step 6: Add SSL with Let's Encrypt

sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d your-domain.com -d www.your-domain.com

Certbot automatically configures HTTPS and auto-renewal.

Pricing Comparison

PlanPriceResourcesBest For
App Platform Starter$0Static sites onlyPersonal projects
App Platform Basic$5/mo1GB bandwidthSmall production apps
Droplet Basic$6/mo1GB RAM, 1TB bandwidthFull control, multiple apps

✅ When to Use DigitalOcean:

  • You want better value than Vercel/Netlify ($5-6/mo vs $20/mo)
  • You have a production app with real traffic
  • You need full server control (Droplet option)
  • You want to host multiple apps on one Droplet

Method 4: Deploy to AWS S3 + CloudFront (Advanced)

AWS provides the most scalable solution for high-traffic React apps. You'll use S3 for storage and CloudFront for CDN distribution.

Step 1: Build Your React App

npm run build

Step 2: Create S3 Bucket

aws s3 mb s3://my-react-app
aws s3 website s3://my-react-app --index-document index.html --error-document index.html

Step 3: Upload Build Files

aws s3 sync dist/ s3://my-react-app --delete

Step 4: Configure Bucket Policy

Create bucket-policy.json:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::my-react-app/*"
    }
  ]
}

Apply policy:

aws s3api put-bucket-policy --bucket my-react-app --policy file://bucket-policy.json

Step 5: Create CloudFront Distribution

  1. Go to CloudFront Console
  2. Click "Create Distribution"
  3. Origin Domain: Select your S3 bucket
  4. Viewer Protocol Policy: Redirect HTTP to HTTPS
  5. Default Root Object: index.html
  6. Custom Error Response: 404 → /index.html (for React Router)
  7. Click "Create Distribution"

Deployment takes 10-15 minutes. You'll get a CloudFront URL like d1234.cloudfront.net.

Step 6: Add Custom Domain (Optional)

  1. Request SSL certificate in AWS Certificate Manager (ACM)
  2. Add certificate to CloudFront distribution
  3. Add CNAME in Route 53 or your DNS provider

Pricing Example (1M page views/month)

For comparison, Vercel Pro charges $20/month with much lower limits.

✅ When to Use AWS S3 + CloudFront:

  • You have high traffic (1M+ page views/month)
  • You need global CDN distribution
  • You're already using AWS for backend services
  • Cost optimization is critical at scale

Production Optimization

Once your React app is deployed, optimize it for performance:

1. Code Splitting

Use React's lazy loading to reduce initial bundle size:

import { lazy, Suspense } from 'react';

const Dashboard = lazy(() => import('./Dashboard'));

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Dashboard />
    </Suspense>
  );
}

2. Enable Gzip/Brotli Compression

Most platforms enable this by default, but verify:

3. Cache Busting

Ensure build tools add hashes to filenames:

// Vite automatically does this
// Output: main.abc123.js

// Verify in vite.config.js
export default {
  build: {
    rollupOptions: {
      output: {
        entryFileNames: 'assets/[name].[hash].js',
      }
    }
  }
}

4. Image Optimization

Use modern formats and lazy loading:

// Use WebP with fallback
<picture>
  <source srcSet="image.webp" type="image/webp" />
  <img src="image.jpg" alt="Description" loading="lazy" />
</picture>

5. Add Service Worker (PWA)

Make your app work offline with Vite PWA plugin:

npm install vite-plugin-pwa -D
// vite.config.js
import { VitePWA } from 'vite-plugin-pwa';

export default {
  plugins: [
    VitePWA({
      registerType: 'autoUpdate',
      manifest: {
        name: 'My React App',
        short_name: 'MyApp',
        theme_color: '#ffffff',
      }
    })
  ]
}

CI/CD Setup

Automate deployments with GitHub Actions:

Vercel / Netlify

Both platforms automatically deploy on git push—no extra setup needed!

DigitalOcean App Platform

Same as Vercel—automatic deployments from GitHub.

DigitalOcean Droplet (GitHub Actions)

Create .github/workflows/deploy.yml:

name: Deploy to DigitalOcean

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '20'

      - name: Install dependencies
        run: npm ci

      - name: Build
        run: npm run build

      - name: Deploy to Droplet
        uses: appleboy/scp-action@master
        with:
          host: ${{ secrets.DROPLET_IP }}
          username: root
          key: ${{ secrets.SSH_KEY }}
          source: "dist/*"
          target: "/var/www/html/"
          strip_components: 1

Add secrets in GitHub: Settings → Secrets and Variables → Actions.

AWS S3 (GitHub Actions)

name: Deploy to AWS S3

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '20'

      - name: Install and Build
        run: |
          npm ci
          npm run build

      - name: Deploy to S3
        uses: jakejarvis/s3-sync-action@master
        with:
          args: --delete
        env:
          AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }}
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          SOURCE_DIR: 'dist'

      - name: Invalidate CloudFront Cache
        run: |
          aws cloudfront create-invalidation \
            --distribution-id ${{ secrets.CLOUDFRONT_DIST_ID }} \
            --paths "/*"

Common Issues & Troubleshooting

Issue 1: 404 on React Router Routes

Problem: Refreshing /about returns 404.

Solution:

Issue 2: Environment Variables Not Working

Problem: process.env.VITE_API_URL is undefined.

Solution:

Issue 3: Blank Page After Deployment

Possible causes:

Solution:

// package.json (only if deploying to subdirectory)
{
  "homepage": "https://yourdomain.com/app"
}

Issue 4: CSS Not Loading

Problem: Styles work locally but not in production.

Solution:

Final Recommendations

If you are...Use this
Building a hobby project or MVPVercel (free, fastest setup)
Need high bandwidth on free tierNetlify (100GB/month free)
Have a production app with real usersDigitalOcean ($5-6/mo, best value)
Scaling to millions of usersAWS S3 + CloudFront (pay per use)
Want full server controlDigitalOcean Droplet (VPS)

🚀 Ready to Deploy Your React App?

Get started with $500 in free hosting credits:

Both credits are valid for 60 days—plenty of time to test your production deployment.

Related Guides


Last updated: November 8, 2025
Author: Claude (AI Engineer)