Vercel - Complete guide to the platform for frontend developers
What is Vercel and why does it dominate the frontend hosting market?
Vercel is a cloud platform created by the makers of Next.js that has revolutionized the way we deploy frontend applications. It offers zero-config deployment, automatic CI/CD, preview deployments for every PR, serverless functions, and edge computing - all in one place.
Unlike traditional hosting providers, Vercel was designed with the modern frontend development workflow in mind. Every push to the repository automatically triggers a build and deployment, and every Pull Request gets a unique URL for testing.
Why Vercel?
Zero-config deployment
# Literally one command
npm i -g vercel
vercel
# Or connect a GitHub repo - every push = automatic deployPreview Deployments
Every Pull Request automatically gets:
- A unique URL for testing
- A comment in the PR with a link
- The ability to review changes before merge
https://my-app-git-feature-branch-username.vercel.appGlobal Edge Network
Vercel deploys your application to 100+ locations around the world. Users get content from the nearest server.
First deployment
Method 1: CLI
# Install CLI
npm i -g vercel
# Deploy (interactive setup)
vercel
# Deploy to production
vercel --prod
# Deploy with specific configuration
vercel --env NODE_ENV=productionMethod 2: GitHub Integration
- Go to vercel.com
- Click "Add New Project"
- Select a repository from GitHub
- Vercel will automatically detect the framework and configure the build
Method 3: Import from CLI
# Clone and deploy right away
npx create-next-app@latest my-app
cd my-app
vercelVercel project structure
my-project/
├── app/ # Next.js App Router
├── pages/ # Next.js Pages Router
├── api/ # API Routes (serverless functions)
├── public/ # Static files
├── vercel.json # Vercel configuration (optional)
└── package.jsonvercel.json configuration
{
"buildCommand": "npm run build",
"outputDirectory": ".next",
"devCommand": "npm run dev",
"installCommand": "npm install",
"framework": "nextjs",
"regions": ["iad1", "cdg1"],
"headers": [
{
"source": "/api/(.*)",
"headers": [
{ "key": "Access-Control-Allow-Origin", "value": "*" },
{ "key": "Access-Control-Allow-Methods", "value": "GET,POST,PUT,DELETE" }
]
},
{
"source": "/(.*)",
"headers": [
{ "key": "X-Content-Type-Options", "value": "nosniff" },
{ "key": "X-Frame-Options", "value": "DENY" }
]
}
],
"redirects": [
{
"source": "/old-blog/:slug",
"destination": "/blog/:slug",
"permanent": true
},
{
"source": "/twitter",
"destination": "https://twitter.com/mycompany",
"permanent": false
}
],
"rewrites": [
{
"source": "/api/external/:path*",
"destination": "https://external-api.com/:path*"
},
{
"source": "/docs/:match*",
"destination": "https://docs.example.com/:match*"
}
],
"cleanUrls": true,
"trailingSlash": false
}Serverless Functions
Vercel automatically detects and deploys serverless functions from the api/ or app/api/ folder.
API Route (Next.js App Router)
// app/api/users/route.ts
import { NextResponse } from 'next/server'
export async function GET(request: Request) {
const { searchParams } = new URL(request.url)
const id = searchParams.get('id')
// Your logic
const users = await fetchUsers(id)
return NextResponse.json(users)
}
export async function POST(request: Request) {
const body = await request.json()
// Your logic
const user = await createUser(body)
return NextResponse.json(user, { status: 201 })
}Standalone Serverless Function
// api/hello.ts (standalone, without Next.js)
import type { VercelRequest, VercelResponse } from '@vercel/node'
export default function handler(req: VercelRequest, res: VercelResponse) {
const { name = 'World' } = req.query
res.status(200).json({
message: `Hello ${name}!`
})
}Function configuration
// api/slow-function.ts
export const config = {
maxDuration: 60, // Max 60 seconds (Pro plan)
memory: 1024, // 1GB RAM
}
export default async function handler(req, res) {
// Long-running operation
}Edge Functions
Edge Functions run on Cloudflare Workers - ultra fast, global, with minimal cold start.
// app/api/geo/route.ts
export const runtime = 'edge'
export async function GET(request: Request) {
// Access geo data from request headers
const country = request.headers.get('x-vercel-ip-country')
const city = request.headers.get('x-vercel-ip-city')
const region = request.headers.get('x-vercel-ip-country-region')
return Response.json({
country,
city,
region,
greeting: getLocalizedGreeting(country)
})
}
function getLocalizedGreeting(country: string | null) {
const greetings: Record<string, string> = {
PL: 'Cześć!',
US: 'Hello!',
DE: 'Hallo!',
FR: 'Bonjour!',
ES: 'Hola!'
}
return greetings[country || 'US'] || 'Hello!'
}Edge Middleware
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
// Check auth token
const token = request.cookies.get('auth-token')
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url))
}
// A/B testing
const bucket = Math.random() < 0.5 ? 'a' : 'b'
const response = NextResponse.next()
response.cookies.set('ab-bucket', bucket)
// Geo-based routing
const country = request.geo?.country || 'US'
if (country === 'DE' && request.nextUrl.pathname === '/') {
return NextResponse.redirect(new URL('/de', request.url))
}
return response
}
export const config = {
matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)']
}Environment Variables
Dashboard
- Go to Project Settings -> Environment Variables
- Add variables for each environment (Production, Preview, Development)
CLI
# Add a variable
vercel env add DATABASE_URL production
# List variables
vercel env ls
# Pull variables to .env.local
vercel env pull .env.localVariable types
# Production-only secrets
DATABASE_URL=postgresql://...
API_SECRET=super-secret-key
# Public (available on client-side)
NEXT_PUBLIC_API_URL=https://api.example.com
# Preview-specific
NEXT_PUBLIC_PREVIEW_MODE=trueDomains and SSL
Custom Domain
# Add a domain via CLI
vercel domains add example.com
# Or in the dashboard: Project Settings → DomainsAutomatic SSL
Vercel automatically:
- Generates an SSL certificate
- Renews the certificate before expiration
- Handles redirect from HTTP to HTTPS
Wildcard domains
*.example.com → Handles all subdomainsAnalytics and Monitoring
Web Analytics
# Installation
npm i @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 i @vercel/speed-insights// app/layout.tsx
import { SpeedInsights } from '@vercel/speed-insights/next'
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<SpeedInsights />
</body>
</html>
)
}Logs
# Real-time logs
vercel logs --follow
# Production logs
vercel logs --production
# Filtering
vercel logs --since 1hCron Jobs
// vercel.json
{
"crons": [
{
"path": "/api/daily-digest",
"schedule": "0 8 * * *"
},
{
"path": "/api/cleanup",
"schedule": "0 0 * * 0"
}
]
}// app/api/daily-digest/route.ts
export async function GET(request: Request) {
// Check if this is a cron request
const authHeader = request.headers.get('authorization')
if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
return new Response('Unauthorized', { status: 401 })
}
// Your cron job logic
await sendDailyDigestEmails()
return Response.json({ success: true })
}Integrations
Database (Vercel Postgres)
# Create a database in the dashboard or CLI
vercel postgres create my-database
# Connect to the project
vercel link
vercel env pull// lib/db.ts
import { sql } from '@vercel/postgres'
export async function getUsers() {
const { rows } = await sql`SELECT * FROM users`
return rows
}Blob Storage
import { put, del, list } from '@vercel/blob'
// Upload
const blob = await put('avatars/user-123.png', file, {
access: 'public'
})
// Delete
await del(blob.url)
// List
const { blobs } = await list({ prefix: 'avatars/' })KV (Redis)
import { kv } from '@vercel/kv'
// Set
await kv.set('user:123', { name: 'Alice' })
await kv.set('session:abc', 'data', { ex: 3600 }) // TTL 1h
// Get
const user = await kv.get('user:123')
// Hash
await kv.hset('user:123', { visits: 1 })
await kv.hincrby('user:123', 'visits', 1)Edge Config
import { get } from '@vercel/edge-config'
// Ultra-fast configuration reading
const featureFlags = await get('feature-flags')
if (featureFlags?.newCheckout) {
// New checkout
}Monorepo Support
Turborepo
// turbo.json
{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "!.next/cache/**"]
},
"lint": {},
"dev": {
"cache": false,
"persistent": true
}
}
}# Structure
apps/
├── web/ # Next.js app
├── docs/ # Documentation site
└── admin/ # Admin panel
packages/
├── ui/ # Shared components
├── config/ # Shared config
└── database/ # Prisma clientVercel Monorepo Settings
In the dashboard: Project Settings -> Root Directory
apps/webfor the web projectapps/docsfor documentation
CI/CD Customization
Ignore Build Step
# vercel-ignore-build-step.sh
#!/bin/bash
# If only docs were changed, skip the build
if git diff --quiet HEAD^ HEAD -- docs/; then
echo "No changes in source code. Skipping build."
exit 0
fi
exit 1// vercel.json
{
"ignoreCommand": "bash vercel-ignore-build-step.sh"
}Custom Build
// vercel.json
{
"buildCommand": "npm run build:production",
"installCommand": "npm ci --legacy-peer-deps",
"framework": null
}Pricing (2025)
| Plan | Price | Bandwidth | Serverless | Team |
|---|---|---|---|---|
| Hobby | Free | 100GB | 100GB-hrs | 1 user |
| Pro | $20/user/mo | 1TB | 1000GB-hrs | Unlimited |
| Enterprise | Custom | Unlimited | Unlimited | Custom |
Hobby Limits
- 100GB bandwidth/month
- 100 deployments/day
- Serverless function timeout: 10s
- Edge function timeout: 30s
- No team features
Pro Features
- 1TB bandwidth
- Serverless timeout: 60s
- Password protection
- Team collaboration
- Priority support
- Advanced analytics
Vercel vs Alternatives
| Aspect | Vercel | Netlify | Railway | AWS Amplify |
|---|---|---|---|---|
| Next.js Support | Native | Adapter | Adapter | Adapter |
| Edge Functions | Yes | Yes | No | Yes |
| Preview Deploys | Yes | Yes | Yes | Yes |
| Pricing | Per-user | Per-site | Usage-based | Usage-based |
| Database | Postgres, KV | None | Postgres | DynamoDB |
| Best for | Next.js | Static/JAMstack | Full-stack | AWS ecosystem |
Best Practices
1. Optimize build time
// next.config.js
module.exports = {
// Use standalone output for smaller deployments
output: 'standalone',
// Disable source maps in production
productionBrowserSourceMaps: false,
}2. Cache dependencies
Vercel automatically caches node_modules, but you can control this:
// vercel.json
{
"build": {
"env": {
"VERCEL_FORCE_NO_BUILD_CACHE": "1"
}
}
}3. Use regions
// Specify a region for functions
export const config = {
regions: ['iad1', 'cdg1'] // US East, France
}4. Monitor usage
- Check the dashboard regularly
- Set alerts for approaching limits
- Analyze functions consuming the most resources
Troubleshooting
Build fails
# Check local builds
npm run build
# Check logs
vercel logs --followFunction timeout
// Increase timeout (Pro plan required)
export const config = {
maxDuration: 60
}
// Or use Edge for faster cold starts
export const runtime = 'edge'Cache issues
# Force rebuild without cache
vercel --force
# Or in the dashboard: Redeploy with cleared cacheFAQ
Can I use Vercel with frameworks other than Next.js?
Yes! Vercel supports: React, Vue, Nuxt, Svelte, SvelteKit, Astro, Remix, Gatsby, Angular, and many others.
How do I migrate from another hosting provider?
- Connect your repo to Vercel
- Configure environment variables
- Add a custom domain
- Change DNS to Vercel
Is Vercel expensive?
The free tier is very generous for personal projects. Pro ($20/user/mo) is the standard for commercial projects. Enterprise is for large companies.
Can I self-host Next.js instead of Vercel?
Yes, Next.js can be hosted on any Node.js server. However, Vercel offers the best integration and many features out of the box.
Summary
Vercel is the best platform for Next.js projects and modern frontend development:
- Zero-config - Deploy in seconds
- Preview Deployments - Test every PR
- Edge Network - Globally fast
- Serverless - No server management
- Integrations - Database, blob, KV, analytics