- Learn
- Stack Essentials
- Next.js
- Deployment & Production
Deploy your Next.js application to Vercel and other platforms with production-ready configuration.
Deployment & Production
Getting your AI-built Next.js application into production requires understanding deployment options, environment configuration, and performance optimization.
Deployment Options
Vercel (Recommended)
The creators of Next.js—seamless integration:
# Install Vercel CLI
npm install -g vercel
# Deploy from project root
vercel
# Deploy to production
vercel --prod
Or connect via GitHub:
- Push to GitHub
- Visit vercel.com
- Import repository
- Automatic deploys on push
Netlify
Supports Next.js with some limitations:
# Install Netlify CLI
npm install -g netlify-cli
# Deploy
netlify deploy --prod
Self-Hosted (Node.js)
Build and run on your own server:
# Build
npm run build
# Start production server
npm start
Docker deployment:
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
ENV NODE_ENV=production
EXPOSE 3000
CMD ["npm", "start"]
Static Export
For static hosting (GitHub Pages, S3):
// next.config.js
const nextConfig = {
output: 'export',
}
module.exports = nextConfig
npm run build
# Output in 'out' directory
Limitations:
- No Server Components (become static)
- No API routes
- No ISR/revalidation
- No image optimization
Environment Variables
Types of Variables
.env # All environments
.env.local # Local overrides (gitignored)
.env.development # Development only
.env.production # Production only
Server vs Client Variables
# .env.local
# Server-side only (secure)
DATABASE_URL="postgresql://..."
API_SECRET="secret-key"
# Exposed to browser (public)
NEXT_PUBLIC_API_URL="https://api.example.com"
NEXT_PUBLIC_STRIPE_KEY="pk_live_..."
In code:
// Server Component - access all variables
const dbUrl = process.env.DATABASE_URL
// Client Component - only NEXT_PUBLIC_*
const apiUrl = process.env.NEXT_PUBLIC_API_URL
Vercel Environment Variables
- Go to Project Settings → Environment Variables
- Add variables with scope:
- Production
- Preview
- Development
Validation
// lib/env.ts
import { z } from 'zod'
const envSchema = z.object({
DATABASE_URL: z.string().url(),
NEXTAUTH_SECRET: z.string().min(32),
NEXT_PUBLIC_API_URL: z.string().url(),
})
export const env = envSchema.parse({
DATABASE_URL: process.env.DATABASE_URL,
NEXTAUTH_SECRET: process.env.NEXTAUTH_SECRET,
NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL,
})
Build Configuration
next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
// Output type
output: 'standalone', // For Docker
// Image domains
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'images.example.com',
},
],
},
// Redirects
async redirects() {
return [
{
source: '/old-page',
destination: '/new-page',
permanent: true,
},
]
},
// Rewrites (proxy)
async rewrites() {
return [
{
source: '/api/:path*',
destination: 'https://api.example.com/:path*',
},
]
},
// Headers
async headers() {
return [
{
source: '/(.*)',
headers: [
{
key: 'X-Frame-Options',
value: 'DENY',
},
],
},
]
},
}
module.exports = nextConfig
TypeScript Strict Mode
// tsconfig.json
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true
}
}
Performance Optimization
Image Optimization
import Image from 'next/image'
// Optimized automatically
<Image
src="/hero.jpg"
alt="Hero"
width={1200}
height={600}
priority // Load immediately (LCP)
/>
// Remote images
<Image
src="https://external.com/image.jpg"
alt="External"
width={400}
height={300}
/>
Font Optimization
// app/layout.tsx
import { Inter } from 'next/font/google'
const inter = Inter({
subsets: ['latin'],
display: 'swap',
})
export default function RootLayout({ children }) {
return (
<html lang="en" className={inter.className}>
<body>{children}</body>
</html>
)
}
Code Splitting
Automatic with dynamic imports:
import dynamic from 'next/dynamic'
// Load component only when needed
const HeavyChart = dynamic(() => import('./HeavyChart'), {
loading: () => <p>Loading chart...</p>,
ssr: false, // Client-side only
})
Bundle Analysis
# Install analyzer
npm install @next/bundle-analyzer
# next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
})
module.exports = withBundleAnalyzer(nextConfig)
# Run analysis
ANALYZE=true npm run build
Database Connections
Connection Pooling
For serverless environments:
// lib/db.ts
import { PrismaClient } from '@prisma/client'
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined
}
export const prisma = globalForPrisma.prisma ?? new PrismaClient()
if (process.env.NODE_ENV !== 'production') {
globalForPrisma.prisma = prisma
}
Serverless Database Services
- Vercel Postgres: Native integration
- PlanetScale: MySQL-compatible, serverless
- Neon: Serverless Postgres
- Supabase: Postgres with extras
Monitoring & Analytics
Vercel Analytics
npm install @vercel/analytics
// app/layout.tsx
import { Analytics } from '@vercel/analytics/react'
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<Analytics />
</body>
</html>
)
}
Speed Insights
npm install @vercel/speed-insights
import { SpeedInsights } from '@vercel/speed-insights/next'
// In layout.tsx
<SpeedInsights />
Error Monitoring
Sentry integration:
npx @sentry/wizard@latest -i nextjs
Security Checklist
Before Going Live
□ Environment variables secured
□ API routes protected
□ CORS configured
□ Rate limiting in place
□ Input validation on all forms
□ SQL injection prevented (use ORM)
□ XSS prevention (React handles most)
□ CSRF tokens for mutations
□ Secure cookies (httpOnly, secure)
□ Content Security Policy headers
Security Headers
// next.config.js
async headers() {
return [
{
source: '/(.*)',
headers: [
{
key: 'X-Content-Type-Options',
value: 'nosniff',
},
{
key: 'X-Frame-Options',
value: 'DENY',
},
{
key: 'X-XSS-Protection',
value: '1; mode=block',
},
{
key: 'Referrer-Policy',
value: 'strict-origin-when-cross-origin',
},
],
},
]
}
Deployment Workflow
Git-Based Deployment
1. Push to main → Deploy to production
2. Push to feature branch → Deploy preview
3. Pull request → Preview URL for review
Preview Deployments
Every PR gets a unique URL:
https://my-app-abc123.vercel.app
Rollback
# Via Vercel CLI
vercel rollback
# Or via dashboard
Deployments → Previous → "..." → Promote to Production
AI Tool Deployment Integration
Lovable/Bolt Deployment
These tools often provide one-click deployment:
- Connect GitHub repository
- Link Vercel account
- Click "Deploy"
Claude Code Deployment
You: Deploy this app to Vercel
Claude Code:
├── Checks vercel.json configuration
├── Validates environment variables
├── Runs: vercel --prod
└── Reports deployment URL
Common Deployment Issues
Issue: Build fails
→ Check for TypeScript errors
→ Verify all environment variables set
→ Check next.config.js for issues
Issue: API routes 404
→ Verify route.ts file structure
→ Check for export mistakes
→ Confirm dynamic routes syntax
Issue: Images not loading
→ Add domains to next.config.js
→ Check image path/URL
→ Verify image file exists
Summary
- Vercel: Best for Next.js, zero config
- Environment variables: Use NEXT_PUBLIC_ for client-side
- Optimization: Images, fonts, dynamic imports
- Database: Use connection pooling for serverless
- Monitoring: Analytics, Speed Insights, error tracking
- Security: Headers, validation, protected routes
Module Complete
You now understand the Next.js essentials for AI development:
- ✅ Introduction to Next.js
- ✅ App Router & File Structure
- ✅ Server vs Client Components
- ✅ Data Fetching Patterns
- ✅ Deployment & Production
Continue with Tailwind CSS to learn the styling system that pairs with Next.js in most AI-generated applications.