Next.js 16
Three Next.js apps on Next 16 with Turbopack and the renamed `proxy.ts` middleware file. What changed from Next 15 and what to watch for.
apps/web, apps/admin, and apps/webview-bridge-docs / apps/dev-docs all
run Next.js 16 with Turbopack. The catalog pins next: ^16.2.6.
Turbopack
Turbopack is now the default next dev bundler in Next 16. The repo opts in
explicitly in each docs site:
// apps/dev-docs/package.json
"scripts": {
"dev": "next dev --turbopack -p 4070"
}For apps/web, next dev already defaults to Turbopack in 16; the dev script
hides this behind the per-worktree port scanner in
scripts/dev-server.mjs.
For next build, apps/web still uses webpack explicitly
(next build --webpack) because some of the production-only bundle
optimizations differ enough that we want stability there. The docs sites use
Turbopack for next build because they ship pure MDX with no JS heavy lifting.
You will see this warning during builds:
⚠ Warning: Next.js inferred your workspace root, but it may not be correct.
Detected additional lockfiles: ...This appears when you build from a .worktrees/ checkout — Next sees two
pnpm-workspace.yaml files (the main checkout and the worktree). It is
harmless; the main checkout's lockfile wins for resolution but the worktree's
sources are what get built. Setting turbopack.root in next.config.ts would
silence it; we accept the warning to keep the config minimal.
middleware.ts → proxy.ts
Next 16 renamed the conventional middleware filename. The exported function is the same shape; only the filename and the exported name changed:
// apps/web/src/proxy.ts (Next 16)
import { NextRequest, NextResponse } from 'next/server'
export default function proxy(request: NextRequest) {
if (!request.cookies.has('better-auth.session_token') && request.nextUrl.pathname === '/') {
return NextResponse.rewrite(new URL('/waitlist', request.nextUrl))
}
return NextResponse.next()
}
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
}The runtime is Node-only under the new name — Edge runtime is no longer the implicit default. If you need Edge for a specific proxy, opt in explicitly in the matcher config (we haven't needed to so far).
apps/web/src/proxy.ts is intentionally tiny — just one rewrite for
unauthenticated visitors hitting /. Anything beyond that lives in route
handlers or layouts.
File-system convention summary
| Next 15 file | Next 16 equivalent |
|---|---|
middleware.ts / middleware.js | proxy.ts / proxy.js |
export default function middleware | export default function proxy |
(everything else under app/) | unchanged |
The repo's docs sites also use proxy.ts (see
apps/dev-docs/proxy.ts)
for Fumadocs' markdown-content-negotiation rewrites.
Configuration
apps/dev-docs/next.config.ts
is the smallest possible Next 16 config in this repo:
import { createMDX } from 'fumadocs-mdx/next'
const withMDX = createMDX()
const config = {
reactStrictMode: true,
typescript: {
tsconfigPath: './tsconfig.app.json',
},
}
export default withMDX(config)typescript.tsconfigPath is set to ./tsconfig.app.json because the project
splits its tsconfig — see tsconfig references. Next
needs to know which one drives the build.
React 19
The catalog pins react: ^19.2.6 and react-dom: ^19.2.6. Server components,
server actions, and the new use() hook are all in scope. Notable runtime
behaviors that bit us during the upgrade:
useEffectdependency arrays are now type-checked more strictly underreact-hooks/exhaustive-deps. Several places had to add deps explicitly.Suspenseboundaries during streaming SSR are sensitive to throwing components above the boundary. A loose fetch error will crash the page rather than fall through to a boundary if there isn't one strictly above.
When in doubt, run pnpm dev and watch the browser console — React 19's
warnings are explicit and actionable.
Migration debt
A few low-priority cleanups remain from the Next 15 → 16 jump:
- Some
metadataexports still usestringfortemplateinstead ofMetadata['title']. Works; minor type churn. - A few
Routetyped-route imports are still pinned to the experimental v15 export shape. Compiles fine; should follow up when we touch those routes.
Both tracked in TODO.md.