# 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**: ```typescript // ❌ 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**: ```typescript type Params> = Promise ``` --- ## 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**: ```typescript // ❌ 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**: ```typescript // ❌ 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**: ```typescript // ❌ 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**: ```typescript // 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**: ```typescript // ❌ 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: ```typescript // ✅ Add 'use client' at the top 'use client' import { useState } from 'react' export function Counter() { const [count, setCount] = useState(0) return } ``` --- ## Error #8: `middleware.ts` is deprecated **Warning Message**: ``` Warning: middleware.ts is deprecated. Use proxy.ts instead. ``` **Solution**: Migrate to `proxy.ts`: ```bash # 1. Rename file mv middleware.ts proxy.ts # 2. Rename function # middleware → proxy ``` **Code**: ```typescript // ✅ 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): ```bash 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`: ```typescript 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: ```typescript // ❌ Wrong 'use client' import { ServerComponent } from './server-component' export function ClientComponent() { return } // ✅ Correct 'use client' export function ClientComponent({ children }: { children: React.ReactNode }) { return
{children}
} // Usage {/* Pass as children */} ``` --- ## Error #12: `generateStaticParams` not working **Error Message**: ``` generateStaticParams is not generating static pages ``` **Cause**: Missing `dynamic = 'force-static'`. **Solution**: ```typescript 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"`: ```typescript '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()`: ```typescript export async function generateMetadata({ params }: { params: Promise<{ id: string }> }): Promise { 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**: ```typescript import { Inter } from 'next/font/google' const inter = Inter({ subsets: ['latin'], variable: '--font-inter' }) export default function RootLayout({ children }: { children: React.ReactNode }) { return ( {/* ✅ Apply variable */} {children} ) } ``` --- ## 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_`: ```bash # .env SECRET_KEY=abc123 # Server-only NEXT_PUBLIC_API_URL=https://api # Available in browser ``` ```typescript // 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**: ```typescript // ❌ 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](#error-1-params-is-a-promise) | | Async searchParams | Add `await searchParams` | [#2](#error-2-searchparams-is-a-promise) | | Async cookies() | Add `await cookies()` | [#3](#error-3-cookies-requires-await) | | Async headers() | Add `await headers()` | [#4](#error-4-headers-requires-await) | | Missing default.js | Create `default.tsx` | [#5](#error-5-parallel-route-missing-defaultjs) | | revalidateTag 1 arg | Add `cacheLife` argument | [#6](#error-6-revalidatetag-requires-2-arguments) | | Hooks in Server Component | Add `'use client'` | [#7](#error-7-cannot-use-react-hooks-in-server-component) | | middleware.ts deprecated | Rename to `proxy.ts` | [#8](#error-8-middlewarets-is-deprecated) | | Turbopack failure | Use `--webpack` flag | [#9](#error-9-turbopack-build-failure) | | Invalid image src | Add `remotePatterns` | [#10](#error-10-invalid-nextimage-src) | | Import Server in Client | Pass as children | [#11](#error-11-cannot-import-server-component-into-client-component) | | generateStaticParams | Add `dynamic = 'force-static'` | [#12](#error-12-generatestaticparams-not-working) | | fetch not caching | Add `'use cache'` | [#13](#error-13-fetch-not-caching) | | Route collision | Use unique paths | [#14](#error-14-route-collision-with-route-groups) | | Metadata not updating | Use `generateMetadata()` | [#15](#error-15-metadata-not-updating) | | Font not loading | Apply font variable to `` | [#16](#error-16-nextfont-font-not-loading) | | Env vars in browser | Prefix with `NEXT_PUBLIC_` | [#17](#error-17-environment-variables-not-available-in-browser) | | Server Action not found | Add `'use server'` | [#18](#error-18-server-action-not-found) | --- ## Prevention Checklist Before deploying, check: - [ ] All `params` are awaited - [ ] All `searchParams` are 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.ts` migrated to `proxy.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 `` or `` - [ ] Public env vars prefixed with `NEXT_PUBLIC_` - [ ] Server Actions have `'use server'` - [ ] Node.js version is 20.9+ --- ## Debugging Tips ### Enable TypeScript Strict Mode ```json // tsconfig.json { "compilerOptions": { "strict": true, "noUncheckedIndexedAccess": true } } ``` ### Check Build Output ```bash npm run build ``` Look for warnings and errors in build logs. ### Use Type Checking ```bash npx tsc --noEmit ``` ### Check Runtime Logs ```bash 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