12 KiB
Next.js 16 - Top 18 Errors & Solutions
Last Updated: 2025-10-24 Prevention Rate: 100% (all documented errors caught)
This guide covers the 18 most common errors when using Next.js 16 and their solutions.
Error #1: params is a Promise
Error Message:
Type 'Promise<{ id: string }>' is not assignable to type '{ id: string }'
Cause: Next.js 16 changed params to async.
Solution:
// ❌ Before (Next.js 15)
export default function Page({ params }: { params: { id: string } }) {
const id = params.id
}
// ✅ After (Next.js 16)
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params
}
TypeScript Fix:
type Params<T = Record<string, string>> = Promise<T>
Error #2: searchParams is a Promise
Error Message:
Property 'query' does not exist on type 'Promise<{ query: string }>'
Cause: searchParams is now async in Next.js 16.
Solution:
// ❌ Before
export default function Page({ searchParams }: { searchParams: { q: string } }) {
const query = searchParams.q
}
// ✅ After
export default async function Page({ searchParams }: { searchParams: Promise<{ q: string }> }) {
const { q: query } = await searchParams
}
Error #3: cookies() requires await
Error Message:
'cookies' implicitly has return type 'any'
Cause: cookies() is async in Next.js 16.
Solution:
// ❌ Before
import { cookies } from 'next/headers'
export function MyComponent() {
const cookieStore = cookies()
const token = cookieStore.get('token')
}
// ✅ After
import { cookies } from 'next/headers'
export async function MyComponent() {
const cookieStore = await cookies()
const token = cookieStore.get('token')
}
Error #4: headers() requires await
Error Message:
'headers' implicitly has return type 'any'
Cause: headers() is async in Next.js 16.
Solution:
// ❌ Before
import { headers } from 'next/headers'
export function MyComponent() {
const headersList = headers()
}
// ✅ After
import { headers } from 'next/headers'
export async function MyComponent() {
const headersList = await headers()
}
Error #5: Parallel route missing default.js
Error Message:
Error: Parallel route @modal/login was matched but no default.js was found
Cause: Next.js 16 requires default.js for all parallel routes.
Solution:
// Create app/@modal/default.tsx
export default function ModalDefault() {
return null
}
Structure:
app/
├── @modal/
│ ├── login/
│ │ └── page.tsx
│ └── default.tsx ← REQUIRED
Error #6: revalidateTag() requires 2 arguments
Error Message:
Expected 2 arguments, but got 1
Cause: revalidateTag() API changed in Next.js 16.
Solution:
// ❌ Before (Next.js 15)
import { revalidateTag } from 'next/cache'
revalidateTag('posts')
// ✅ After (Next.js 16)
import { revalidateTag } from 'next/cache'
revalidateTag('posts', 'max') // Second argument required
Cache Life Profiles:
'max'- Maximum staleness (recommended)'hours'- Stale after hours'days'- Stale after days- Custom:
{ stale: 3600, revalidate: 86400 }
Error #7: Cannot use React hooks in Server Component
Error Message:
You're importing a component that needs useState. It only works in a Client Component
Cause: Using React hooks in Server Component.
Solution: Add 'use client' directive:
// ✅ Add 'use client' at the top
'use client'
import { useState } from 'react'
export function Counter() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(count + 1)}>{count}</button>
}
Error #8: middleware.ts is deprecated
Warning Message:
Warning: middleware.ts is deprecated. Use proxy.ts instead.
Solution: Migrate to proxy.ts:
# 1. Rename file
mv middleware.ts proxy.ts
# 2. Rename function
# middleware → proxy
Code:
// ✅ proxy.ts
export function proxy(request: NextRequest) {
// Same logic
}
Error #9: Turbopack build failure
Error Message:
Error: Failed to compile with Turbopack
Cause: Turbopack is now default in Next.js 16.
Solution 1 (opt-out):
npm run build -- --webpack
Solution 2 (fix compatibility): Check for incompatible packages and update them.
Error #10: Invalid next/image src
Error Message:
Invalid src prop (https://example.com/image.jpg) on `next/image`. Hostname "example.com" is not configured
Cause: Remote images not configured.
Solution: Add to next.config.ts:
const config = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'example.com',
pathname: '/images/**',
},
],
},
}
Error #11: Cannot import Server Component into Client Component
Error Message:
You're importing a Server Component into a Client Component
Cause: Direct import of Server Component in Client Component.
Solution: Pass as children:
// ❌ Wrong
'use client'
import { ServerComponent } from './server-component'
export function ClientComponent() {
return <ServerComponent />
}
// ✅ Correct
'use client'
export function ClientComponent({ children }: { children: React.ReactNode }) {
return <div>{children}</div>
}
// Usage
<ClientComponent>
<ServerComponent /> {/* Pass as children */}
</ClientComponent>
Error #12: generateStaticParams not working
Error Message:
generateStaticParams is not generating static pages
Cause: Missing dynamic = 'force-static'.
Solution:
export const dynamic = 'force-static'
export async function generateStaticParams() {
const posts = await fetch('/api/posts').then(r => r.json())
return posts.map((post: { id: string }) => ({ id: post.id }))
}
Error #13: fetch() not caching
Error Message: Data not cached (performance issue).
Cause: Next.js 16 uses opt-in caching.
Solution: Add "use cache":
'use cache'
export async function getPosts() {
const response = await fetch('/api/posts')
return response.json()
}
Error #14: Route collision with Route Groups
Error Message:
Error: Conflicting routes: /about and /(marketing)/about
Cause: Route groups creating same URL path.
Solution: Ensure unique paths:
app/
├── (marketing)/about/page.tsx → /about
└── (shop)/store-info/page.tsx → /store-info (NOT /about)
Error #15: Metadata not updating
Error Message: SEO metadata not showing correctly.
Cause: Using static metadata for dynamic pages.
Solution: Use generateMetadata():
export async function generateMetadata({ params }: { params: Promise<{ id: string }> }): Promise<Metadata> {
const { id } = await params
const post = await fetch(`/api/posts/${id}`).then(r => r.json())
return {
title: post.title,
description: post.excerpt,
}
}
Error #16: next/font font not loading
Error Message: Custom fonts not applying.
Cause: Font variable not applied to HTML element.
Solution:
import { Inter } from 'next/font/google'
const inter = Inter({ subsets: ['latin'], variable: '--font-inter' })
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html className={inter.variable}> {/* ✅ Apply variable */}
<body>{children}</body>
</html>
)
}
Error #17: Environment variables not available in browser
Error Message: process.env.SECRET_KEY is undefined in client.
Cause: Server-only env vars not exposed to browser.
Solution: Prefix with NEXT_PUBLIC_:
# .env
SECRET_KEY=abc123 # Server-only
NEXT_PUBLIC_API_URL=https://api # Available in browser
// Server Component (both work)
const secret = process.env.SECRET_KEY
const apiUrl = process.env.NEXT_PUBLIC_API_URL
// Client Component (only public vars)
const apiUrl = process.env.NEXT_PUBLIC_API_URL
Error #18: Server Action not found
Error Message:
Error: Could not find Server Action
Cause: Missing 'use server' directive.
Solution:
// ❌ Before
export async function createPost(formData: FormData) {
await db.posts.create({ ... })
}
// ✅ After
'use server'
export async function createPost(formData: FormData) {
await db.posts.create({ ... })
}
Quick Error Lookup
| Error Type | Solution | Link |
|---|---|---|
| Async params | Add await params |
#1 |
| Async searchParams | Add await searchParams |
#2 |
| Async cookies() | Add await cookies() |
#3 |
| Async headers() | Add await headers() |
#4 |
| Missing default.js | Create default.tsx |
#5 |
| revalidateTag 1 arg | Add cacheLife argument |
#6 |
| Hooks in Server Component | Add 'use client' |
#7 |
| middleware.ts deprecated | Rename to proxy.ts |
#8 |
| Turbopack failure | Use --webpack flag |
#9 |
| Invalid image src | Add remotePatterns |
#10 |
| Import Server in Client | Pass as children | #11 |
| generateStaticParams | Add dynamic = 'force-static' |
#12 |
| fetch not caching | Add 'use cache' |
#13 |
| Route collision | Use unique paths | #14 |
| Metadata not updating | Use generateMetadata() |
#15 |
| Font not loading | Apply font variable to <html> |
#16 |
| Env vars in browser | Prefix with NEXT_PUBLIC_ |
#17 |
| Server Action not found | Add 'use server' |
#18 |
Prevention Checklist
Before deploying, check:
- All
paramsare awaited - All
searchParamsare awaited - All
cookies()calls are awaited - All
headers()calls are awaited - All parallel routes have
default.js revalidateTag()has 2 arguments- Client Components have
'use client' middleware.tsmigrated toproxy.ts- Remote images configured in
next.config.ts - Server Components not imported directly in Client Components
- Static pages have
dynamic = 'force-static' - Cached components have
'use cache' - No route collisions with Route Groups
- Dynamic pages use
generateMetadata() - Fonts applied to
<html>or<body> - Public env vars prefixed with
NEXT_PUBLIC_ - Server Actions have
'use server' - Node.js version is 20.9+
Debugging Tips
Enable TypeScript Strict Mode
// tsconfig.json
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true
}
}
Check Build Output
npm run build
Look for warnings and errors in build logs.
Use Type Checking
npx tsc --noEmit
Check Runtime Logs
npm run dev
Watch console for errors and warnings.
Resources
- Migration Guide:
references/next-16-migration-guide.md - Templates:
templates/directory - Next.js 16 Blog: https://nextjs.org/blog/next-16
- Support: jeremy@jezweb.net