Complete Next.js deployment system. PROACTIVELY activate for: (1) Vercel deployment and configuration, (2) Self-hosted Node.js deployment, (3) Docker containerization, (4) Static export with output:'export', (5) Edge Runtime configuration, (6) Environment variables, (7) CI/CD with GitHub Actions, (8) Health checks and monitoring, (9) Production optimization. Provides: Dockerfile, docker-compose, PM2 config, vercel.json, GitHub Actions workflows. Ensures production-ready deployment with proper configuration.
/plugin marketplace add JosiahSiegel/claude-plugin-marketplace/plugin install nextjs-master@claude-plugin-marketplaceThis skill inherits all available tools. When active, it can use any tool Claude has access to.
| Output Mode | Config | Use Case |
|---|---|---|
| Default | - | Vercel/Node.js hosting |
| Standalone | output: 'standalone' | Docker containers |
| Static | output: 'export' | Static hosting (no SSR) |
| Platform | Command | Notes |
|---|---|---|
| Vercel | vercel --prod | Automatic, recommended |
| Node.js | npm run build && npm start | Self-hosted |
| Docker | docker build -t app . | Container deployment |
| Static | npm run build → out/ | CDN hosting |
| Runtime | Config | Features |
|---|---|---|
| Node.js | Default | Full features |
| Edge | export const runtime = 'edge' | Fast, limited APIs |
| Environment | File | Usage |
|---|---|---|
| Development | .env.local | Local dev |
| Production | .env.production | Build time |
| Runtime | Platform secrets | Server runtime |
Use for deployment and production:
Related skills:
nextjs-middlewarenextjs-cachingnextjs-authentication# Install Vercel CLI
npm i -g vercel
# Deploy
vercel
# Production deployment
vercel --prod
# Set environment variables
vercel env add DATABASE_URL production
# Pull env vars locally
vercel env pull .env.local
{
"buildCommand": "npm run build",
"outputDirectory": ".next",
"framework": "nextjs",
"regions": ["iad1", "sfo1"],
"functions": {
"app/api/**/*.ts": {
"memory": 1024,
"maxDuration": 30
}
},
"headers": [
{
"source": "/api/(.*)",
"headers": [
{ "key": "Access-Control-Allow-Origin", "value": "*" }
]
}
],
"redirects": [
{
"source": "/old-page",
"destination": "/new-page",
"permanent": true
}
],
"rewrites": [
{
"source": "/api/proxy/:path*",
"destination": "https://api.example.com/:path*"
}
]
}
# Build the application
npm run build
# Start production server
npm run start
// server.ts
import { createServer } from 'http';
import { parse } from 'url';
import next from 'next';
const dev = process.env.NODE_ENV !== 'production';
const hostname = process.env.HOSTNAME || 'localhost';
const port = parseInt(process.env.PORT || '3000', 10);
const app = next({ dev, hostname, port });
const handle = app.getRequestHandler();
app.prepare().then(() => {
createServer(async (req, res) => {
try {
const parsedUrl = parse(req.url!, true);
await handle(req, res, parsedUrl);
} catch (err) {
console.error('Error occurred handling', req.url, err);
res.statusCode = 500;
res.end('Internal Server Error');
}
})
.once('error', (err) => {
console.error(err);
process.exit(1);
})
.listen(port, () => {
console.log(`> Ready on http://${hostname}:${port}`);
});
});
// ecosystem.config.js
module.exports = {
apps: [
{
name: 'nextjs-app',
script: 'npm',
args: 'start',
instances: 'max',
exec_mode: 'cluster',
autorestart: true,
watch: false,
max_memory_restart: '1G',
env: {
NODE_ENV: 'production',
PORT: 3000,
},
},
],
};
# Start with PM2
pm2 start ecosystem.config.js
# Monitor
pm2 monit
# Logs
pm2 logs
# Dockerfile
FROM node:20-alpine AS base
# Dependencies stage
FROM base AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json package-lock.json* ./
RUN npm ci
# Builder stage
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NEXT_TELEMETRY_DISABLED 1
RUN npm run build
# Runner stage
FROM base AS runner
WORKDIR /app
ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
# Automatically leverage output traces
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT 3000
ENV HOSTNAME "0.0.0.0"
CMD ["node", "server.js"]
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'standalone',
};
module.exports = nextConfig;
# docker-compose.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- '3000:3000'
environment:
- DATABASE_URL=postgresql://user:password@db:5432/mydb
- NEXTAUTH_SECRET=your-secret
- NEXTAUTH_URL=http://localhost:3000
depends_on:
- db
db:
image: postgres:15-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: mydb
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'export',
// Optional: Change the output directory
distDir: 'dist',
// Optional: Add trailing slashes
trailingSlash: true,
// Optional: Disable image optimization
images: {
unoptimized: true,
},
};
module.exports = nextConfig;
npm run build
# Output in 'out' directory (or distDir if configured)
// app/api/edge/route.ts
export const runtime = 'edge';
export async function GET(request: Request) {
return new Response(JSON.stringify({ message: 'Hello from Edge!' }), {
headers: { 'content-type': 'application/json' },
});
}
// middleware.ts
// Runs on Edge by default
import { NextResponse } from 'next/server';
export function middleware(request: Request) {
return NextResponse.next();
}
import { get } from '@vercel/edge-config';
export const runtime = 'edge';
export async function GET() {
const greeting = await get('greeting');
return Response.json({ greeting });
}
# .env.production
DATABASE_URL=
NEXTAUTH_SECRET=
NEXTAUTH_URL=https://myapp.com
NEXT_PUBLIC_API_URL=https://api.myapp.com
/** @type {import('next').NextConfig} */
const nextConfig = {
// Strict mode for development
reactStrictMode: true,
// Optimize images
images: {
domains: ['images.example.com'],
formats: ['image/avif', 'image/webp'],
},
// Security headers
async headers() {
return [
{
source: '/:path*',
headers: [
{ key: 'X-Frame-Options', value: 'DENY' },
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
],
},
];
},
// Redirects
async redirects() {
return [
{
source: '/old-blog/:slug',
destination: '/blog/:slug',
permanent: true,
},
];
},
// Experimental features
experimental: {
// Enable PPR
ppr: true,
},
};
module.exports = nextConfig;
// app/api/health/route.ts
import { NextResponse } from 'next/server';
export async function GET() {
try {
// Check database connection
await db.$queryRaw`SELECT 1`;
return NextResponse.json({
status: 'healthy',
timestamp: new Date().toISOString(),
});
} catch (error) {
return NextResponse.json(
{ status: 'unhealthy', error: 'Database connection failed' },
{ status: 503 }
);
}
}
// next.config.js
const { withSentryConfig } = require('@sentry/nextjs');
const nextConfig = {
// Your config
};
module.exports = withSentryConfig(nextConfig, {
silent: true,
org: 'your-org',
project: 'your-project',
});
// sentry.client.config.ts
import * as Sentry from '@sentry/nextjs';
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
tracesSampleRate: 1.0,
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
});
// app/layout.tsx
import { Analytics } from '@vercel/analytics/react';
import { SpeedInsights } from '@vercel/speed-insights/next';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html>
<body>
{children}
<Analytics />
<SpeedInsights />
</body>
</html>
);
}
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Build
run: npm run build
- name: Deploy to Vercel
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args: '--prod'
| Practice | Description |
|---|---|
| Use output: 'standalone' | Smaller Docker images |
| Set proper env vars | Use Vercel/platform secrets |
| Enable caching | Use CDN, browser caching |
| Monitor performance | Vercel Analytics, Sentry |
| Use health checks | Load balancer integration |
| Enable compression | Gzip/Brotli in reverse proxy |
Use when working with Payload CMS projects (payload.config.ts, collections, fields, hooks, access control, Payload API). Use when debugging validation errors, security issues, relationship queries, transactions, or hook behavior.
Applies Anthropic's official brand colors and typography to any sort of artifact that may benefit from having Anthropic's look-and-feel. Use it when brand colors or style guidelines, visual formatting, or company design standards apply.
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.