π Lesson 10.4: Build and Deployment
Take your React TypeScript applications from development to production. Learn how to build optimized production bundles, configure environment variables, deploy to popular platforms, and set up continuous integration and deployment pipelines.
π― Learning Objectives
By the end of this lesson, you will be able to:
- Create optimized production builds with Vite
- Configure and use environment variables securely
- Deploy React applications to Vercel, Netlify, and other platforms
- Set up custom domains with SSL certificates
- Implement CI/CD pipelines with GitHub Actions
- Monitor application performance in production
- Handle common deployment issues and troubleshooting
Estimated Time: 75-90 minutes
Project: Deploy your first React app to production
π In This Lesson
π Introduction to Production Deployment
Congratulations! You've built amazing React TypeScript applications throughout this course. Now it's time for the most exciting stepβsharing your work with the world by deploying to production.
π What is Deployment?
Deployment is the process of moving your application from your local development environment to a production server where real users can access it over the internet. This involves building optimized code, configuring servers, and ensuring everything works correctly in the production environment.
π― Development vs Production
Understanding the differences between development and production environments is crucial:
| Aspect | Development | Production |
|---|---|---|
| Goal | Fast feedback, easy debugging | Performance, stability, security |
| Build Size | Large (includes dev tools) | Optimized (minified, tree-shaken) |
| Error Messages | Detailed, helpful warnings | User-friendly, minimal details |
| Source Maps | Enabled for debugging | Optional or disabled |
| Hot Reload | Enabled for fast iteration | Not needed |
| Environment | Local machine (localhost) | Remote server (public URL) |
π The Deployment Pipeline
Modern deployment follows a structured pipeline:
β Why Use Modern Deployment Platforms?
Modern platforms like Vercel and Netlify make deployment incredibly easy:
- Zero configuration: Connect your GitHub repo and deploy
- Automatic builds: New commits trigger automatic deployments
- Global CDN: Fast loading times worldwide
- Free SSL: Automatic HTTPS for security
- Preview deployments: Test changes before going live
- Rollback support: Easy to revert to previous versions
π¦ Creating Production Builds
Before deploying, you need to create an optimized production build of your application. This process transforms your development code into highly optimized files ready for production.
π What is a Production Build?
A production build is an optimized version of your application where code is minified, compressed, and stripped of development tools. This results in smaller file sizes and faster load times for your users.
π¨ Building with Vite
If you're using Vite (recommended for React TypeScript projects), building is straightforward:
# Build for production
npm run build
# Preview the production build locally
npm run preview
This creates a dist folder containing your optimized application:
dist/
βββ assets/
β βββ index-a1b2c3d4.js # Your bundled JavaScript
β βββ index-e5f6g7h8.css # Your bundled CSS
β βββ logo-i9j0k1l2.svg # Optimized assets
βββ index.html # Entry point
βββ favicon.ico # Site icon
π‘ What Happens During Build?
- Minification: Removes whitespace, shortens variable names
- Tree shaking: Removes unused code from bundles
- Code splitting: Creates separate chunks for better caching
- Asset optimization: Compresses images and other assets
- TypeScript compilation: Converts TS to optimized JS
- CSS bundling: Combines and minifies stylesheets
βοΈ Vite Configuration
You can customize the build process in vite.config.ts:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
// Build options
build: {
// Output directory
outDir: 'dist',
// Generate source maps for debugging
sourcemap: false, // Set to true for debugging production
// Minification
minify: 'esbuild', // Fast and efficient
// Target browsers
target: 'es2015',
// Chunk size warnings
chunkSizeWarningLimit: 1000,
// Rollup options for advanced configuration
rollupOptions: {
output: {
// Manual chunk splitting for better caching
manualChunks: {
'react-vendor': ['react', 'react-dom'],
'router': ['react-router-dom'],
},
},
},
},
// Base path for deployment
base: '/', // Change if deploying to a subdirectory
// Preview server configuration
preview: {
port: 3000,
host: true,
},
});
π Analyzing Bundle Size
Understanding what's in your bundle helps optimize it:
# Install bundle analyzer
npm install --save-dev rollup-plugin-visualizer
# Build and analyze
npm run build
Add to vite.config.ts:
import { visualizer } from 'rollup-plugin-visualizer';
export default defineConfig({
plugins: [
react(),
visualizer({
open: true,
filename: 'dist/stats.html',
gzipSize: true,
brotliSize: true,
}),
],
});
β οΈ Common Build Issues
- TypeScript errors: Fix all type errors before building
- Missing dependencies: Ensure all packages are in package.json
- Path issues: Use relative imports, not absolute paths
- Environment variables: Configure properly (next section)
- Large bundle size: Use code splitting and lazy loading
π― Build Best Practices
// package.json scripts
{
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"lint": "eslint . --ext ts,tsx",
"type-check": "tsc --noEmit"
}
}
β Pre-Deployment Checklist
- β
Run
npm run buildsuccessfully - β
Test with
npm run preview - β Check for TypeScript errors
- β Verify bundle size is reasonable
- β Test in different browsers
- β Confirm all environment variables are set
- β Review console for errors/warnings
π Environment Variables
Environment variables allow you to configure your application differently for development, staging, and production without changing code. They're essential for managing API keys, feature flags, and environment-specific settings.
π What are Environment Variables?
Environment variables are key-value pairs that exist outside your codebase. They let you keep sensitive information (like API keys) out of version control and configure your app for different environments without code changes.
π Using Environment Variables in Vite
Vite has built-in support for environment variables through .env files:
# .env.local (for local development - NOT committed to git)
VITE_API_URL=http://localhost:3001/api
VITE_API_KEY=dev_key_12345
VITE_ENABLE_ANALYTICS=false
# .env.production (for production - committed to git)
VITE_API_URL=https://api.myapp.com
VITE_ENABLE_ANALYTICS=true
# Note: Never put secret keys in .env.production!
β οΈ Important: VITE_ Prefix Required
In Vite, environment variables must start with VITE_ to be exposed to your app. This is a security feature to prevent accidentally exposing server-side secrets to the client.
π Accessing Environment Variables
// src/config/env.ts
interface Config {
apiUrl: string;
apiKey: string;
enableAnalytics: boolean;
environment: 'development' | 'production';
}
export const config: Config = {
apiUrl: import.meta.env.VITE_API_URL || 'http://localhost:3001/api',
apiKey: import.meta.env.VITE_API_KEY || '',
enableAnalytics: import.meta.env.VITE_ENABLE_ANALYTICS === 'true',
environment: import.meta.env.MODE as 'development' | 'production',
};
// Validate required variables
if (!config.apiUrl) {
throw new Error('VITE_API_URL is required');
}
// Log config in development only
if (import.meta.env.DEV) {
console.log('App Configuration:', config);
}
π Security Best Practices
β DO's
- β
Use
.env.localfor local secrets (add to .gitignore) - β Use platform environment variables for production secrets
- β Document required variables in README
- β Validate environment variables on app startup
- β Use different API keys for dev/staging/production
β DON'Ts
- β Never commit
.env.localto version control - β Never put secret keys in
.env.production - β Don't expose server-side secrets to client code
- β Don't use production API keys in development
- β Never hardcode secrets in your source code
π .gitignore Configuration
# .gitignore
# Environment files with secrets
.env.local
.env.*.local
# Build output
dist
dist-ssr
*.local
# Dependencies
node_modules
# Logs
logs
*.log
# IDE
.vscode/*
.idea
π― TypeScript Support
Add type definitions for better autocomplete:
// src/vite-env.d.ts
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_API_URL: string;
readonly VITE_API_KEY: string;
readonly VITE_ENABLE_ANALYTICS: string;
// Add more env variables here
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
π Example: API Client with Environment Variables
// src/lib/api-client.ts
import { config } from '@/config/env';
class ApiClient {
private baseUrl: string;
private apiKey: string;
constructor() {
this.baseUrl = config.apiUrl;
this.apiKey = config.apiKey;
}
async fetch<T>(endpoint: string, options?: RequestInit): Promise<T> {
const url = `${this.baseUrl}${endpoint}`;
const response = await fetch(url, {
...options,
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`,
...options?.headers,
},
});
if (!response.ok) {
throw new Error(`API Error: ${response.statusText}`);
}
return response.json();
}
async get<T>(endpoint: string): Promise<T> {
return this.fetch<T>(endpoint);
}
async post<T>(endpoint: string, data: unknown): Promise<T> {
return this.fetch<T>(endpoint, {
method: 'POST',
body: JSON.stringify(data),
});
}
}
export const apiClient = new ApiClient();
π Different Environments
# Development
npm run dev
# Uses .env.local and .env.development
# Production build
npm run build
# Uses .env.production
# Custom environment
npm run build -- --mode staging
# Uses .env.staging
π Environment Variables Documentation
Create a .env.example file to document required variables:
# .env.example
# Copy this to .env.local and fill in your values
# API Configuration
VITE_API_URL=http://localhost:3001/api
VITE_API_KEY=your_api_key_here
# Feature Flags
VITE_ENABLE_ANALYTICS=false
VITE_ENABLE_DARK_MODE=true
# Third-party Services
VITE_STRIPE_PUBLIC_KEY=pk_test_...
VITE_GOOGLE_ANALYTICS_ID=G-...
βοΈ Deployment Platforms Overview
There are many platforms for deploying React applications. Let's compare the most popular options to help you choose the right one for your needs.
π Popular Deployment Platforms
| Platform | Best For | Free Tier | Key Features |
|---|---|---|---|
| Vercel | Next.js, React SPAs | Generous (100GB bandwidth) | Zero config, preview deployments, edge functions |
| Netlify | Static sites, JAMstack | Good (100GB bandwidth) | Forms, serverless functions, split testing |
| GitHub Pages | Simple static sites | Unlimited (public repos) | Free, simple, direct from repo |
| Cloudflare Pages | Global performance | Unlimited bandwidth | Fast CDN, Workers integration |
| AWS Amplify | AWS ecosystem apps | Limited (12 months) | Full AWS integration, scalability |
| Render | Full-stack apps | Basic (free tier) | Static sites + backends, databases |
π― Choosing the Right Platform
π‘ Decision Factors
- Use Vercel if: You want the best Next.js support or need edge functions
- Use Netlify if: You need built-in forms or A/B testing features
- Use GitHub Pages if: You want simple, free hosting from your repo
- Use Cloudflare Pages if: You need global CDN performance
- Use AWS Amplify if: You're already using AWS services
- Use Render if: You need both frontend and backend hosting
β¨ Common Features Across Platforms
π° Pricing Comparison
| Platform | Free Tier | Pro Tier (Monthly) | Bandwidth Limit |
|---|---|---|---|
| Vercel | Yes | $20/member | 100GB (free) / 1TB (pro) |
| Netlify | Yes | $19/member | 100GB (free) / 1TB (pro) |
| GitHub Pages | Yes (unlimited) | N/A | 100GB/month (soft limit) |
| Cloudflare Pages | Yes | $20/month | Unlimited (free & paid) |
| Render | Yes (limited) | Starts at $7 | 100GB/month (free) |
β Recommendation for Beginners
Start with Vercel or Netlify. Both offer:
- Excellent free tiers for learning and small projects
- Simple deployment process (connect GitHub repo)
- Automatic deployments on every git push
- Great documentation and community support
- Easy custom domain setup
β² Deploying to Vercel
Vercel is one of the most popular platforms for deploying React applications. Created by the makers of Next.js, it offers an exceptional developer experience with zero-configuration deployments.
π Why Vercel?
Vercel specializes in frontend frameworks and offers the fastest way to deploy React applications. It handles everything from building your app to serving it on a global CDN, all with automatic HTTPS and preview deployments.
π Deploying Your First App to Vercel
Step 1: Prepare Your Repository
# Ensure your code is in a Git repository
git init
git add .
git commit -m "Initial commit"
# Push to GitHub (or GitLab/Bitbucket)
git remote add origin https://github.com/yourusername/your-repo.git
git push -u origin main
Step 2: Sign Up for Vercel
- Go to vercel.com
- Click "Sign Up"
- Choose "Continue with GitHub" (recommended)
- Authorize Vercel to access your repositories
Step 3: Import Your Project
- Click "Add New Project"
- Select your repository from the list
- Vercel auto-detects Vite and configures build settings
- Click "Deploy"
β What Vercel Detects Automatically
- Framework: Vite (from package.json)
- Build Command:
npm run build - Output Directory:
dist - Node Version: Latest LTS
βοΈ Vercel Configuration
Create vercel.json for custom configuration:
{
"buildCommand": "npm run build",
"outputDirectory": "dist",
"devCommand": "npm run dev",
"installCommand": "npm install",
"framework": "vite",
"env": {
"VITE_API_URL": "https://api.myapp.com"
},
"headers": [
{
"source": "/assets/(.*)",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=31536000, immutable"
}
]
}
],
"redirects": [
{
"source": "/old-page",
"destination": "/new-page",
"permanent": true
}
],
"rewrites": [
{
"source": "/api/:path*",
"destination": "https://api.myapp.com/:path*"
}
]
}
π Setting Environment Variables
- Go to your project settings on Vercel
- Navigate to "Environment Variables"
- Add your variables (one per environment)
# Production
VITE_API_URL=https://api.myapp.com
VITE_API_KEY=prod_key_xyz
# Preview (for PR deployments)
VITE_API_URL=https://staging-api.myapp.com
VITE_API_KEY=staging_key_abc
# Development (optional)
VITE_API_URL=http://localhost:3001
VITE_API_KEY=dev_key_123
π Automatic Deployments
Once connected, Vercel automatically deploys:
π‘ Preview Deployments
Every pull request gets its own unique preview URL:
- Test changes before merging
- Share with team for review
- Automatic comments on GitHub PRs with preview link
- Isolated environment with its own database/API
π± Vercel CLI (Optional)
Deploy from the command line:
# Install Vercel CLI globally
npm install -g vercel
# Login to Vercel
vercel login
# Deploy to production
vercel --prod
# Deploy preview
vercel
π Deploying to Netlify
Netlify is another excellent platform for deploying React applications, known for its powerful features like form handling, serverless functions, and split testing.
π Why Netlify?
Netlify offers a complete platform for modern web projects. Beyond hosting, it provides built-in forms, serverless functions, and advanced features like split testingβall with a generous free tier.
π Deploying Your First App to Netlify
Method 1: Via Web UI (Easiest)
- Go to netlify.com and sign up
- Click "Add new site" β "Import an existing project"
- Connect to GitHub and select your repository
- Configure build settings:
- Build command:
npm run build - Publish directory:
dist
- Build command:
- Click "Deploy site"
Method 2: Drag and Drop
# Build locally
npm run build
# Drag the dist folder to netlify.com/drop
Method 3: Netlify CLI
# Install Netlify CLI
npm install -g netlify-cli
# Login
netlify login
# Initialize Netlify in your project
netlify init
# Deploy
netlify deploy --prod
βοΈ Netlify Configuration
Create netlify.toml in your project root:
# netlify.toml
[build]
command = "npm run build"
publish = "dist"
[build.environment]
NODE_VERSION = "18"
# Redirect rules
[[redirects]]
from = "/api/*"
to = "https://api.myapp.com/:splat"
status = 200
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
# Headers
[[headers]]
for = "/assets/*"
[headers.values]
Cache-Control = "public, max-age=31536000, immutable"
[[headers]]
for = "/*"
[headers.values]
X-Frame-Options = "DENY"
X-Content-Type-Options = "nosniff"
X-XSS-Protection = "1; mode=block"
π Environment Variables in Netlify
- Go to Site settings β Environment variables
- Add your variables
- Choose scopes (Production, Deploy previews, Branch deploys)
# Via Netlify UI or CLI
VITE_API_URL=https://api.myapp.com
VITE_API_KEY=your_key_here
β¨ Netlify Unique Features
1. Built-in Forms
<!-- Add netlify attribute to form -->
<form name="contact" method="POST" data-netlify="true">
<input type="hidden" name="form-name" value="contact" />
<input type="text" name="name" required />
<input type="email" name="email" required />
<textarea name="message" required></textarea>
<button type="submit">Send</button>
</form>
2. Serverless Functions
// netlify/functions/hello.ts
import { Handler } from '@netlify/functions';
export const handler: Handler = async (event, context) => {
return {
statusCode: 200,
body: JSON.stringify({ message: 'Hello from Netlify!' }),
};
};
// Access at: https://yoursite.netlify.app/.netlify/functions/hello
3. Split Testing
# netlify.toml
[[redirects]]
from = "/*"
to = "/branch-a/:splat"
status = 200
conditions = { Cookie = ["split-test=a"] }
[[redirects]]
from = "/*"
to = "/branch-b/:splat"
status = 200
conditions = { Cookie = ["split-test=b"] }
β Netlify Best Practices
- Use
netlify.tomlfor configuration (version controlled) - Enable "Build plugins" for optimization (image optimization, etc.)
- Use deploy contexts for different environments
- Set up deploy notifications (Slack, email)
- Use Netlify Analytics for privacy-friendly analytics
π Custom Domains and SSL
Your deployed app gets a free subdomain (like myapp.vercel.app), but you'll want a custom domain for a professional look. Both Vercel and Netlify make this easy with automatic SSL certificates.
π What is a Custom Domain?
A custom domain is your own web address (like myapp.com) instead of using a platform's subdomain. SSL/TLS certificates enable HTTPS encryption, making your site secure and trusted by browsers.
π Buying a Domain
Popular domain registrars:
| Registrar | Price Range | Features |
|---|---|---|
| Namecheap | $8-15/year | Good prices, free WHOIS protection |
| Google Domains | $12/year | Simple, reliable, good support |
| Cloudflare | At-cost (cheapest) | No markup, requires account |
| Porkbun | $5-10/year | Budget-friendly, good features |
π Connecting Domain to Vercel
Step 1: Add Domain in Vercel
- Go to Project Settings β Domains
- Enter your domain (e.g.,
myapp.com) - Vercel provides DNS records to add
Step 2: Configure DNS
Add these records at your domain registrar:
| Type | Name | Value |
|---|---|---|
| A | @ | 76.76.21.21 |
| CNAME | www | cname.vercel-dns.com |
π‘ DNS Propagation
DNS changes can take 24-48 hours to propagate globally, but often complete within minutes. Use whatsmydns.net to check propagation status.
π Connecting Domain to Netlify
Option 1: Use Netlify DNS (Recommended)
- Go to Site settings β Domain management
- Click "Add custom domain"
- Choose "Use Netlify DNS"
- Update nameservers at your registrar:
dns1.p0x.netlify.comdns2.p0x.netlify.comdns3.p0x.netlify.comdns4.p0x.netlify.com
Option 2: External DNS
Add these records at your DNS provider:
| Type | Name | Value |
|---|---|---|
| A | @ | 75.2.60.5 |
| CNAME | www | your-site.netlify.app |
π SSL Certificates
Both platforms provide free, automatic SSL certificates:
β SSL is Automatic!
- Vercel: SSL cert issued automatically within minutes
- Netlify: SSL cert issued via Let's Encrypt automatically
- Auto-renewal: Certificates renew automatically before expiring
- HTTPS redirect: HTTP automatically redirects to HTTPS
- No cost: Completely free, no manual configuration needed
π― Subdomain and Apex Domain
# Apex domain (root domain)
myapp.com
# www subdomain
www.myapp.com
# Custom subdomains
api.myapp.com
blog.myapp.com
docs.myapp.com
β οΈ Best Practice: Support Both
Configure both myapp.com and www.myapp.com, then set one as primary with automatic redirect from the other. Most platforms handle this automatically.
π Domain Configuration Checklist
Click to expand checklist
- β Domain purchased from registrar
- β DNS records configured correctly
- β DNS propagation completed (check whatsmydns.net)
- β SSL certificate issued
- β HTTPS working (look for padlock in browser)
- β HTTP redirects to HTTPS
- β www subdomain configured
- β Primary domain selected
- β Test site loads correctly on custom domain
βοΈ CI/CD with GitHub Actions
Continuous Integration and Continuous Deployment (CI/CD) automate testing and deployment. Every time you push code, your tests run automatically, and if they pass, your app deploys to production.
π What is CI/CD?
CI/CD is the practice of automating your development workflow. Continuous Integration (CI) runs tests on every commit. Continuous Deployment (CD) automatically deploys passing code to production.
π― Why Use CI/CD?
β Benefits
- Catch bugs early: Tests run automatically on every commit
- Consistent builds: Same process every time
- Faster releases: Deploy with confidence, no manual steps
- Team coordination: Everyone follows the same workflow
- Rollback capability: Easy to revert if something goes wrong
π§ GitHub Actions Basics
GitHub Actions is a CI/CD platform built into GitHub. It runs workflows defined in YAML files:
# .github/workflows/deploy.yml
name: Deploy to Production
# Trigger on push to main branch
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
# Checkout code
- uses: actions/checkout@v3
# Setup Node.js
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
# Install dependencies
- name: Install dependencies
run: npm ci
# Run type check
- name: Type check
run: npm run type-check
# Run linter
- name: Lint
run: npm run lint
# Run tests
- name: Run tests
run: npm test
# Build for production
- name: Build
run: npm run build
env:
VITE_API_URL: ${{ secrets.VITE_API_URL }}
VITE_API_KEY: ${{ secrets.VITE_API_KEY }}
π Advanced Workflow Example
# .github/workflows/ci-cd.yml
name: CI/CD Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
# Allow manual trigger
workflow_dispatch:
jobs:
# Job 1: Test
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16, 18, 20]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests with coverage
run: npm run test:coverage
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
file: ./coverage/coverage-final.json
# Job 2: Build and Deploy
deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
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: Build
run: npm run build
env:
VITE_API_URL: ${{ secrets.VITE_API_URL }}
VITE_API_KEY: ${{ secrets.VITE_API_KEY }}
# Deploy to Vercel
- name: Deploy to Vercel
uses: amondnet/vercel-action@v20
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args: '--prod'
π Managing Secrets
Store sensitive information in GitHub Secrets:
- Go to your repository on GitHub
- Settings β Secrets and variables β Actions
- Click "New repository secret"
- Add your secrets:
VITE_API_URLVITE_API_KEYVERCEL_TOKEN- etc.
π‘ Using Secrets in Workflows
env:
VITE_API_URL: ${{ secrets.VITE_API_URL }}
VITE_API_KEY: ${{ secrets.VITE_API_KEY }}
π Workflow Status Badges
Add a badge to your README to show workflow status:

π― Common Workflow Patterns
1. Preview Deployments for PRs
on:
pull_request:
types: [opened, synchronize]
jobs:
preview:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Deploy Preview
# Deploy to preview environment
run: vercel --env=preview
2. Scheduled Workflows
on:
schedule:
# Run every day at midnight
- cron: '0 0 * * *'
jobs:
nightly-build:
runs-on: ubuntu-latest
steps:
# Build and test
3. Multi-Environment Deployment
jobs:
deploy-staging:
if: github.ref == 'refs/heads/develop'
# Deploy to staging
deploy-production:
if: github.ref == 'refs/heads/main'
# Deploy to production
β CI/CD Best Practices
- Run tests before deploying
- Use caching to speed up workflows
- Keep secrets in GitHub Secrets, never in code
- Use matrix builds to test multiple Node versions
- Add status checks to protect main branch
- Use workflow approvals for production deploys
- Monitor workflow run times and optimize
π Performance Monitoring
After deployment, it's crucial to monitor your application's performance in production. Real users experience different conditions than your local development environment.
π Why Monitor Production?
Performance monitoring helps you understand how your app performs for real users. It tracks metrics like load time, errors, and user interactions, allowing you to identify and fix issues quickly.
π Key Metrics to Monitor
| Metric | What It Measures | Good Target |
|---|---|---|
| LCP (Largest Contentful Paint) | Loading performance | < 2.5s |
| FID (First Input Delay) | Interactivity | < 100ms |
| CLS (Cumulative Layout Shift) | Visual stability | < 0.1 |
| TTFB (Time to First Byte) | Server response | < 600ms |
| FCP (First Contentful Paint) | Perceived load speed | < 1.8s |
π οΈ Monitoring Tools
1. Built-in Platform Analytics
// Vercel Analytics
import { Analytics } from '@vercel/analytics/react';
function App() {
return (
<>
<YourApp />
<Analytics />
</>
);
}
// Netlify Analytics
// Automatically enabled, no code needed
// View in Netlify dashboard β Analytics
2. Google Lighthouse
Test your site's performance:
- Open Chrome DevTools (F12)
- Go to "Lighthouse" tab
- Select categories (Performance, Accessibility, Best Practices, SEO)
- Click "Analyze page load"
π‘ Lighthouse Scoring
- 90-100: Good (green)
- 50-89: Needs improvement (orange)
- 0-49: Poor (red)
3. Web Vitals Library
# Install web-vitals
npm install web-vitals
// src/lib/vitals.ts
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';
function sendToAnalytics(metric: any) {
// Send to your analytics service
console.log(metric);
// Example: Send to Google Analytics
if (window.gtag) {
window.gtag('event', metric.name, {
value: Math.round(metric.value),
event_category: 'Web Vitals',
event_label: metric.id,
non_interaction: true,
});
}
}
// Measure all Web Vitals
getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getFCP(sendToAnalytics);
getLCP(sendToAnalytics);
getTTFB(sendToAnalytics);
// src/main.tsx
import { reportWebVitals } from './lib/vitals';
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// Report web vitals
reportWebVitals();
4. Error Monitoring with Sentry
npm install @sentry/react
// src/main.tsx
import * as Sentry from '@sentry/react';
Sentry.init({
dsn: import.meta.env.VITE_SENTRY_DSN,
environment: import.meta.env.MODE,
integrations: [
new Sentry.BrowserTracing(),
new Sentry.Replay(),
],
// Performance Monitoring
tracesSampleRate: 1.0,
// Session Replay
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
});
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
π± Real User Monitoring (RUM)
Track real user experiences:
// Custom RUM implementation
class RUM {
private metrics: Map<string, number> = new Map();
trackPageView(path: string) {
// Track page views
this.sendEvent('pageview', { path });
}
trackError(error: Error) {
// Track JavaScript errors
this.sendEvent('error', {
message: error.message,
stack: error.stack,
});
}
trackPerformance() {
// Track performance metrics
const navigation = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming;
this.metrics.set('dns', navigation.domainLookupEnd - navigation.domainLookupStart);
this.metrics.set('tcp', navigation.connectEnd - navigation.connectStart);
this.metrics.set('ttfb', navigation.responseStart - navigation.requestStart);
this.metrics.set('download', navigation.responseEnd - navigation.responseStart);
this.metrics.set('domInteractive', navigation.domInteractive - navigation.fetchStart);
this.metrics.set('domComplete', navigation.domComplete - navigation.fetchStart);
this.sendMetrics();
}
private sendEvent(name: string, data: any) {
// Send to your analytics backend
fetch('/api/analytics', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ event: name, data }),
});
}
private sendMetrics() {
// Send collected metrics
fetch('/api/metrics', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(Object.fromEntries(this.metrics)),
});
}
}
export const rum = new RUM();
// Usage
rum.trackPageView(window.location.pathname);
rum.trackPerformance();
π― Monitoring Best Practices
β What to Monitor
- Performance metrics: Core Web Vitals, load times
- Error rates: JavaScript errors, API failures
- User behavior: Page views, interactions, conversions
- Resource loading: Image, script, and stylesheet performance
- API performance: Request/response times, error rates
- Browser/device distribution: What users are using
β οΈ Privacy Considerations
- Respect user privacy (GDPR, CCPA compliance)
- Anonymize IP addresses
- Provide opt-out mechanisms
- Don't track PII without consent
- Use privacy-friendly analytics when possible
π Setting Up Alerts
// Example: Monitor and alert on poor performance
function checkPerformance() {
getLCP((metric) => {
if (metric.value > 2500) {
// Alert: Poor LCP
sendAlert({
metric: 'LCP',
value: metric.value,
threshold: 2500,
severity: 'warning',
});
}
});
getCLS((metric) => {
if (metric.value > 0.1) {
// Alert: Poor CLS
sendAlert({
metric: 'CLS',
value: metric.value,
threshold: 0.1,
severity: 'warning',
});
}
});
}
ποΈ Hands-on Exercises
Let's practice deploying React applications! These exercises will help you gain real-world deployment experience.
π― Exercise 1: Deploy Your First App
Deploy one of your React TypeScript projects to production.
Requirements
- Choose a project from this course (Todo app, Weather dashboard, etc.)
- Create a GitHub repository
- Push your code to GitHub
- Deploy to Vercel or Netlify
- Verify the deployment works
- Share the URL!
Step-by-Step Guide
# 1. Initialize git if not already done
git init
# 2. Add .gitignore
echo "node_modules
dist
.env.local
*.log" > .gitignore
# 3. Commit your code
git add .
git commit -m "Initial commit"
# 4. Create GitHub repo and push
git remote add origin https://github.com/yourusername/your-repo.git
git push -u origin main
# 5. Go to Vercel.com or Netlify.com
# 6. Import your repository
# 7. Deploy!
Success Criteria
- β App deployed and accessible via public URL
- β HTTPS enabled (padlock in browser)
- β All pages/routes work correctly
- β No console errors in production
- β Assets (images, fonts) load properly
π― Exercise 2: Configure Environment Variables
Set up environment variables for development and production.
Tasks
- Create
.env.localfor local development - Create
.env.exampleas documentation - Add at least 3 environment variables
- Configure them in your deployment platform
- Use them in your application code
- Verify different values work in dev vs production
Example Implementation
# .env.local (don't commit!)
VITE_APP_NAME=My Awesome App
VITE_API_URL=http://localhost:3001/api
VITE_ENABLE_DEBUG=true
# .env.example (commit this)
VITE_APP_NAME=Your App Name
VITE_API_URL=https://api.example.com
VITE_ENABLE_DEBUG=false
// src/config/env.ts
export const config = {
appName: import.meta.env.VITE_APP_NAME,
apiUrl: import.meta.env.VITE_API_URL,
enableDebug: import.meta.env.VITE_ENABLE_DEBUG === 'true',
};
// Usage in component
function App() {
return (
<div>
<h1>{config.appName}</h1>
{config.enableDebug && <DebugPanel />}
</div>
);
}
π― Exercise 3: Set Up a Custom Domain
Connect a custom domain to your deployed application.
Steps
Option A: Use a free subdomain service
- Use services like FreeDNS or NoIP
- Get a free subdomain (e.g.,
myapp.mooo.com) - Configure DNS to point to your deployment
Option B: Buy a domain (recommended)
- Purchase domain from Namecheap, Porkbun, or Google Domains
- Configure DNS records as shown in lesson
- Wait for DNS propagation (usually 5-30 minutes)
- Verify SSL certificate is issued
Verification Checklist
- β Domain accessible via browser
- β HTTPS working (green padlock)
- β Both apex and www subdomains work
- β HTTP redirects to HTTPS
- β Check on whatsmydns.net
π― Exercise 4: Implement CI/CD
Set up automated testing and deployment with GitHub Actions.
Requirements
- Create a GitHub Actions workflow file
- Run TypeScript type checking on every push
- Run tests on every pull request
- Automatically deploy on push to main
- Add a status badge to your README
Starter Workflow
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
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: Type check
run: npm run type-check
- name: Lint
run: npm run lint
- name: Build
run: npm run build
Bonus Challenges
- Add test coverage reporting
- Run tests in multiple Node.js versions
- Create preview deployments for PRs
- Add Slack notifications on deployment
- Implement blue-green deployments
π― Exercise 5: Monitor Your App
Add performance monitoring to your production application.
Tasks
- Install and configure
web-vitals - Track Core Web Vitals (LCP, FID, CLS)
- Run Lighthouse audit on deployed site
- Identify and fix at least 3 performance issues
- Compare before/after Lighthouse scores
Implementation Guide
# Install web-vitals
npm install web-vitals
// src/reportWebVitals.ts
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';
export function reportWebVitals() {
getCLS(console.log);
getFID(console.log);
getFCP(console.log);
getLCP(console.log);
getTTFB(console.log);
}
// src/main.tsx
import { reportWebVitals } from './reportWebVitals';
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
reportWebVitals();
Success Metrics
Target Lighthouse Scores:
- Performance: > 90
- Accessibility: > 90
- Best Practices: > 90
- SEO: > 90
Core Web Vitals:
- LCP: < 2.5s
- FID: < 100ms
- CLS: < 0.1
π§ Troubleshooting Common Issues
Deployment doesn't always go smoothly. Here are solutions to common problems you might encounter.
β Problem: Build Fails
Error Message
Error: Command "npm run build" exited with 1
Common Causes & Solutions
- TypeScript errors: Run
npm run type-checklocally and fix all errors - Missing dependencies: Ensure all packages are in
package.json, not just devDependencies - Different Node versions: Specify Node version in deployment config
- Environment variables missing: Check that all required env vars are set
β Problem: 404 on Refresh
Issue
App works fine when navigating from home, but refreshing on a route (e.g., /about) gives 404 error.
Solution: Configure Redirects
For Vercel:
// vercel.json
{
"rewrites": [
{ "source": "/(.*)", "destination": "/" }
]
}
For Netlify:
# netlify.toml
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
Or create public/_redirects:
/* /index.html 200
β Problem: Environment Variables Not Working
Common Issues
- Missing VITE_ prefix: All client-side env vars must start with
VITE_ - Not set on platform: Environment variables must be configured in deployment platform UI
- Quotes in values: Don't use quotes around values in platform UI
- Not rebuilding: Changes to env vars require a new deployment
Debug Steps
// Add this temporarily to check
console.log('Environment:', import.meta.env);
console.log('API URL:', import.meta.env.VITE_API_URL);
β Problem: Slow Build Times
Optimization Strategies
- Cache dependencies: Most platforms cache
node_modulesby default - Reduce bundle size: Use code splitting and tree shaking
- Skip unnecessary steps: Don't run tests in production builds
- Use faster package manager: Try
pnpminstead ofnpm
// Speed up npm ci
{
"scripts": {
"build": "npm ci --prefer-offline && vite build"
}
}
β Problem: Images Not Loading
Common Causes
- Wrong path: Use relative paths or
importstatements - Case sensitivity: Linux servers are case-sensitive (
Image.pngβimage.png) - Not in public folder: Static assets should be in
public/ - Base path issue: Check
baseconfig invite.config.ts
Correct Approaches
// β
Import the image
import logo from './assets/logo.png';
<img src={logo} alt="Logo" />
// β
Use public folder
<img src="/images/logo.png" alt="Logo" />
// β Don't use absolute paths
<img src="C:/Users/me/project/logo.png" alt="Logo" />
β Problem: API Requests Failing
CORS Issues
If API requests work locally but fail in production:
- Backend CORS: Configure your API to allow requests from your domain
- Use proxy: Configure Vite proxy for development
- Use serverless functions: Netlify/Vercel functions can act as proxy
// vite.config.ts - Development proxy
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://localhost:3001',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},
});
β Problem: Large Bundle Size
Reduction Strategies
# Analyze bundle
npm install --save-dev rollup-plugin-visualizer
npm run build
- Lazy load routes: Use React.lazy() for route components
- Remove unused dependencies: Check with
depcheck - Use lighter alternatives: Replace heavy libraries (e.g., moment.js β date-fns)
- Tree shake properly: Import specific functions, not entire libraries
// β Bad - imports entire library
import _ from 'lodash';
// β
Good - imports specific function
import debounce from 'lodash/debounce';
// β
Even better - use native methods when possible
const debounce = (fn: Function, ms: number) => {
let timeout: NodeJS.Timeout;
return (...args: any[]) => {
clearTimeout(timeout);
timeout = setTimeout(() => fn(...args), ms);
};
};
π οΈ General Debugging Tips
β Debugging Checklist
- Check build logs: Read the entire error message carefully
- Build locally first: Run
npm run buildandnpm run preview - Clear cache: Try clearing platform's build cache
- Check environment: Verify all required env vars are set
- Verify dependencies: Make sure package.json is correct
- Test in production mode: Some issues only appear in production builds
- Check browser console: Look for JavaScript errors
- Verify routes: Test all routes and page refreshes
- Check network tab: See if assets are loading correctly
- Compare with working version: Look at what changed since last successful deploy
π Summary
Congratulations! You've learned how to deploy React TypeScript applications to production. Let's recap the essential concepts:
π― Key Takeaways
β What You've Learned
- Production builds: Creating optimized builds with Vite
- Environment variables: Managing configuration securely
- Deployment platforms: Using Vercel, Netlify, and others
- Custom domains: Connecting your own domain with SSL
- CI/CD: Automating testing and deployment
- Monitoring: Tracking performance and errors in production
- Troubleshooting: Fixing common deployment issues
π The Deployment Workflow
π Pre-Deployment Checklist
Complete Deployment Checklist
Before Deployment
- β Code works locally without errors
- β All tests passing
- β TypeScript compiles without errors
- β Production build succeeds (
npm run build) - β Preview build works (
npm run preview) - β Environment variables documented
- β
.env.localin.gitignore - β All secrets removed from code
During Deployment
- β Repository connected to platform
- β Build settings configured correctly
- β Environment variables set on platform
- β Custom domain configured (if applicable)
- β SSL certificate active
- β Redirects configured for SPA routing
After Deployment
- β Site loads correctly
- β All routes work (test page refresh)
- β Images and assets load
- β API calls work
- β Forms submit successfully
- β No console errors
- β HTTPS working (green padlock)
- β Mobile responsive
- β Performance acceptable (Lighthouse > 80)
- β Monitoring configured
π οΈ Essential Tools Reference
| Tool/Platform | Purpose | Link |
|---|---|---|
| Vercel | Deployment platform | vercel.com |
| Netlify | Deployment platform | netlify.com |
| GitHub Actions | CI/CD automation | github.com/features/actions |
| Lighthouse | Performance auditing | Built into Chrome DevTools |
| Sentry | Error monitoring | sentry.io |
| whatsmydns.net | DNS propagation checker | whatsmydns.net |
π Quick Command Reference
Essential Commands
# Build for production
npm run build
# Preview production build locally
npm run preview
# Type check
npm run type-check
# Deploy with Vercel CLI
vercel --prod
# Deploy with Netlify CLI
netlify deploy --prod
# Initialize Vercel project
vercel
# Initialize Netlify project
netlify init
# Check bundle size
npm run build -- --stats
π‘ Final Thoughts
Deployment is not a one-time taskβit's an ongoing process. As you add features, fix bugs, and improve performance, you'll deploy many times. The key is to make this process smooth, automated, and reliable. With the tools and techniques you've learned, you can deploy with confidence!
π What's Next?
You've mastered deployment! Here's how to continue your journey:
π Continue Learning
- Next Lesson: Lesson 10.5 - Next Steps and Advanced Topics
- Practice: Deploy all your course projects to production
- Explore: Try different deployment platforms and compare them
- Learn: Dive deeper into DevOps, Docker, and Kubernetes
π― Immediate Next Steps
π‘ This Week's Challenge
- Deploy at least one project from this course
- Set up a custom domain (even a free subdomain)
- Implement CI/CD with GitHub Actions
- Monitor your app's performance with Lighthouse
- Share your deployed app on social media or with friends!
π Advanced Topics to Explore
β Take It Further
- Server-Side Rendering: Learn Next.js for SSR and SSG
- Edge Functions: Deploy serverless functions at the edge
- Monorepo Management: Use Turborepo or Nx
- Advanced CI/CD: Blue-green deployments, canary releases
- Infrastructure as Code: Terraform, Pulumi
- Container Orchestration: Docker, Kubernetes
- Observability: Logs, metrics, traces with OpenTelemetry
π± Build Your Portfolio
Now that you know how to deploy, showcase your work:
- Portfolio Website: Create a personal site with all your projects
- GitHub Profile: Pin your best repositories
- Blog: Write about what you learned (great for SEO and networking)
- LinkedIn: Share your deployed projects
- Dev.to / Medium: Publish technical articles
π Course Completion
You're almost at the end of the React TypeScript course! After the final lesson, you'll have:
- β Built multiple production-ready React applications
- β Mastered TypeScript for type-safe development
- β Learned modern React patterns and best practices
- β Deployed applications to production
- β Gained real-world development experience
π You're Ready!
With deployment mastered, you now have the complete skill set to build and ship professional React TypeScript applications. You're not just learningβyou're building real products that real people can use. Keep building, keep shipping, and keep learning!