vercel
Deploys applications to Vercel including serverless functions, edge functions, environment variables, and CI/CD. Use when deploying Next.js applications, frontend projects, or serverless APIs.
Installation
Follow the repository instructions for this skill and your agent. Review the files and dependencies before adding it to your workflow.
View source ↗GuidesSkill instructions
Vercel
The frontend cloud platform for deploying web applications.
Quick Start
Install CLI:
npm i -g vercel
Deploy:
vercel
Deploy to production:
vercel --prod
Project Setup
Connect Git Repository
- Go to vercel.com/new
- Import repository from GitHub/GitLab/Bitbucket
- Configure build settings (auto-detected for most frameworks)
- Deploy
vercel.json Configuration
{
"buildCommand": "npm run build",
"outputDirectory": "dist",
"installCommand": "npm install",
"framework": "nextjs",
"regions": ["iad1"],
"functions": {
"api/**/*.ts": {
"memory": 1024,
"maxDuration": 10
}
},
"rewrites": [
{ "source": "/api/:path*", "destination": "/api/:path*" }
],
"redirects": [
{ "source": "/old", "destination": "/new", "permanent": true }
],
"headers": [
{
"source": "/(.*)",
"headers": [
{ "key": "X-Frame-Options", "value": "DENY" }
]
}
]
}
Environment Variables
Setting Variables
Via Dashboard:
- Project Settings > Environment Variables
- Add key-value pairs
- Select environments (Production, Preview, Development)
Via CLI:
vercel env add MY_VAR
vercel env ls
vercel env pull .env.local
Environment Types
// Production only
NEXT_PUBLIC_API_URL=https://api.example.com
// Preview (PR deployments)
NEXT_PUBLIC_API_URL=https://staging-api.example.com
// Development
NEXT_PUBLIC_API_URL=http://localhost:3001
Using Variables
// Next.js - server side
const apiKey = process.env.API_KEY;
// Next.js - client side (must be prefixed)
const publicUrl = process.env.NEXT_PUBLIC_API_URL;
Serverless Functions
API Routes (Next.js App Router)
// app/api/users/route.ts
import { NextRequest, NextResponse } from 'next/server';
export async function GET(request: NextRequest) {
const users = await getUsers();
return NextResponse.json(users);
}
export async function POST(request: NextRequest) {
const body = await request.json();
const user = await createUser(body);
return NextResponse.json(user, { status: 201 });
}
API Routes (Pages Router)
// pages/api/users.ts
import type { NextApiRequest, NextApiResponse } from 'next';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method === 'GET') {
const users = await getUsers();
return res.json(users);
}
if (req.method === 'POST') {
const user = await createUser(req.body);
return res.status(201).json(user);
}
res.setHeader('Allow', ['GET', 'POST']);
res.status(405).end(`Method ${req.method} Not Allowed`);
}
Standalone Functions
// api/hello.ts
import type { VercelRequest, VercelResponse } from '@vercel/node';
export default function handler(req: VercelRequest, res: VercelResponse) {
return res.json({ message: 'Hello from Vercel!' });
}
Function Configuration
// app/api/heavy/route.ts
export const runtime = 'nodejs';
export const maxDuration = 60; // seconds
export const dynamic = 'force-dynamic';
export async function GET() {
// Long-running operation
}
Edge Functions
Edge Runtime
// app/api/geo/route.ts
import { NextRequest, NextResponse } from 'next/server';
export const runtime = 'edge';
export async function GET(request: NextRequest) {
const country = request.geo?.country ?? 'Unknown';
const city = request.geo?.city ?? 'Unknown';
return NextResponse.json({
country,
city,
message: `Hello from ${city}, ${country}!`,
});
}
Middleware
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
// Check auth
const token = request.cookies.get('token');
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url));
}
// Add headers
const response = NextResponse.next();
response.headers.set('x-custom-header', 'value');
return response;
}
export const config = {
matcher: ['/dashboard/:path*', '/api/:path*'],
};
Caching & ISR
Static Generation with Revalidation
// app/posts/page.tsx
export const revalidate = 3600; // Revalidate every hour
async function getPosts() {
const res = await fetch('https://api.example.com/posts', {
next: { revalidate: 3600 },
});
return res.json();
}
export default async function PostsPage() {
const posts = await getPosts();
return <PostList posts={posts} />;
}
On-Demand Revalidation
// app/api/revalidate/route.ts
import { revalidatePath, revalidateTag } from 'next/cache';
import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) {
const { path, tag, secret } = await request.json();
if (secret !== process.env.REVALIDATE_SECRET) {
return NextResponse.json({ error: 'Invalid secret' }, { status: 401 });
}
if (path) {
revalidatePath(path);
}
if (tag) {
revalidateTag(tag);
}
return NextResponse.json({ revalidated: true });
}
Redirects & Rewrites
vercel.json
{
"redirects": [
{ "source": "/blog/:slug", "destination": "/posts/:slug", "permanent": true },
{ "source": "/old-page", "destination": "/new-page", "statusCode": 301 }
],
"rewrites": [
{ "source": "/api/:path*", "destination": "https://api.example.com/:path*" },
{ "source": "/:path*", "destination": "/index.html" }
]
}
Next.js Config
// next.config.js
module.exports = {
async redirects() {
return [
{
source: '/old-blog/:slug',
destination: '/blog/:slug',
permanent: true,
},
];
},
async rewrites() {
return [
{
source: '/api/:path*',
destination: 'https://api.backend.com/:path*',
},
];
},
};
Deployment
Preview Deployments
# Every push to non-production branch creates preview
git push origin feature-branch
# Creates: feature-branch-abc123.vercel.app
Production Deployment
# Via CLI
vercel --prod
# Via Git (push to main/master)
git push origin main
Rollback
# Via CLI
vercel rollback [deployment-url]
# Via Dashboard
# Deployments > ... > Promote to Production
Monitoring
Analytics
// app/layout.tsx
import { Analytics } from '@vercel/analytics/react';
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<Analytics />
</body>
</html>
);
}
Speed Insights
import { SpeedInsights } from '@vercel/speed-insights/next';
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<SpeedInsights />
</body>
</html>
);
}
Cron Jobs
// vercel.json
{
"crons": [
{
"path": "/api/cron/daily",
"schedule": "0 0 * * *"
},
{
"path": "/api/cron/hourly",
"schedule": "0 * * * *"
}
]
}
// app/api/cron/daily/route.ts
import { NextRequest, NextResponse } from 'next/server';
export async function GET(request: NextRequest) {
const authHeader = request.headers.get('authorization');
if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
// Run daily task
await runDailyTask();
return NextResponse.json({ success: true });
}
Best Practices
- Use environment variables - Never commit secrets
- Configure regions - Deploy close to users/data
- Enable preview deployments - Test before production
- Use ISR for dynamic content - Balance freshness and speed
- Add monitoring - Analytics and Speed Insights
Common Mistakes
| Mistake | Fix |
|---|---|
| Exposing secrets | Use env vars, not code |
| Large function bundles | Split into smaller functions |
| Missing NEXT_PUBLIC_ prefix | Prefix client-side vars |
| No error handling | Add try/catch in functions |
| Cold start issues | Use edge runtime when possible |
Reference Files
- references/functions.md - Function patterns
- references/caching.md - Caching strategies
- references/domains.md - Custom domains
Examples
Read the usage examples in the source repository.
View source ↗Source history
- Indexed on
- 2026-01-20
- Index updated
- 2026-01-28
- Branch
- main
Index and sync dates describe this directory. View the source repository for code history.
GitHub ↗chat Comments (0)
Sign in to join the discussion and leave a comment.
No comments yet. Be the first to share your thoughts!