Next.js 16
Four 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/tangible, apps/webview-bridge-docs, and 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
apps/web/scripts/dev-server.mts.
All four apps use Turbopack for next build. apps/web and apps/tangible
also keep explicit build:webpack scripts as rollback paths while their custom
webpack configuration remains supported.
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.