Edge vs Node Runtime
Next.js can run your code on two different runtimes. Understanding when to use each is crucial for production deployments.
The Two Runtimes
| Runtime | Where | Cold Start | APIs | Size Limit |
|---|---|---|---|---|
| Node.js | Server regions | Slower | Full Node.js | None |
| Edge | CDN edge locations | Fast | Limited | 1-4 MB |
Default: Node.js Runtime
By default, everything runs on Node.js:
// This runs on Node.js
export default async function Page() {
const data = await db.query('SELECT * FROM users') // Full database access
return <UserList users={data} />
}
Full access to:
- File system (
fs) - All Node.js APIs
- Any npm package
- Native modules
- Large dependencies
Opting Into Edge Runtime
Set the runtime for specific routes:
// app/api/hello/route.ts
export const runtime = 'edge'
export async function GET() {
return Response.json({ message: 'Hello from the edge!' })
}
// app/dashboard/page.tsx
export const runtime = 'edge'
export default function Dashboard() {
// This renders at the edge
return <div>Dashboard</div>
}
Edge Limitations
The Edge runtime is a subset of Node.js. You cannot use:
fs(file system)- Native Node.js modules
- Most database drivers (need Edge-compatible versions)
- Large packages (size limit ~1-4 MB)
// ❌ This will fail on Edge
export const runtime = 'edge'
import fs from 'fs'
import { nativeModule } from 'native-module'
// ✅ These work on Edge
import { headers, cookies } from 'next/headers'
export async function GET() {
const headersList = await headers()
return Response.json({ ua: headersList.get('user-agent') })
}
Middleware: Always Edge
Middleware runs on the Edge runtime automatically:
// middleware.ts - Always runs at the edge
export function middleware(request: NextRequest) {
// Fast, runs close to the user
return NextResponse.next()
}
You cannot change middleware to Node.js runtime.
Edge-Compatible Packages
Some packages have Edge-specific versions:
| Category | Edge-Compatible |
|---|---|
| Database | Prisma Edge, PlanetScale, Neon |
| Auth | jose (not jsonwebtoken) |
| Crypto | Web Crypto API |
| HTTP | Native fetch |
// ❌ Node.js only
import jwt from 'jsonwebtoken'
// ✅ Edge compatible
import { jwtVerify } from 'jose'
When to Use Each Runtime
Use Edge for:
- Middleware (required)
- Auth checks (JWT verification)
- Redirects and rewrites
- A/B testing
- Geolocation-based logic
- Simple API responses
Use Node.js for:
- Database queries
- File system access
- Complex computations
- Large dependencies
- Server Components (default)
- API routes with heavy logic
Checking Runtime in Build Output
Route (app) Size First Load JS
┌ ○ / 5.2 kB 89.1 kB
├ ℇ /api/edge 0 B 0 B
├ λ /api/node 0 B 0 B
└ ○ /about 2.1 kB 86.0 kB
ℇ (Edge) server-rendered on every request at edge locations
λ (Dynamic) server-rendered on every request at a single region
Hybrid Approach
Use both runtimes strategically:
// middleware.ts - Edge (automatic)
export function middleware(request: NextRequest) {
// Quick auth check, redirect
}
// app/api/users/route.ts - Node.js (default)
export async function GET() {
// Full database access
}
// app/api/ping/route.ts - Edge (explicit)
export const runtime = 'edge'
export async function GET() {
// Simple, fast response
}
Summary
- Node.js: Full API access, single region, slower cold starts
- Edge: Limited APIs, global CDN, fast cold starts
- Middleware is always Edge
- Use Edge for auth checks, redirects, and simple logic
- Use Node.js for database access and complex operations
- Check build output for runtime indicators (ℇ vs λ)

