All Articles
CategoryFull-Stack
Reading Time
15 min read
Published
2026-09-07
Word Count
3,510words

Grab a coffee — this one is a deep dive!

Next.js 16.3 Drops runtime='edge': Back to Node

Summary

Next.js 16.3 removed runtime='edge' support in routes and pages; proxy.ts never supported it in the first place. This guide separates the two and shows the migration steps to Node.js.

  • As of Next.js 16.3, routes and pages no longer support `runtime = 'edge'`; the code now runs on Node.js.
  • proxy.ts (formerly middleware.ts) has used the Node.js default and rejected the `runtime` config since 16.0.0 — a rule from a different version, not 16.3.
  • Migration is usually a one-line cleanup: deleting the `export const runtime = 'edge'` line is enough, since Node.js is already the default.
  • Once edge is gone, fs and native module access opens up, but duration limits become subject to Vercel's Node.js function and Fluid Compute rules.
Next.js 16.3 Drops runtime='edge': Back to Node

With Next.js 16.3, the runtime = 'edge' setting at the route and page level is no longer supported — Vercel's official documentation states this plainly: every route and page using this configuration now runs on the Node.js runtime. If your project still has export const runtime = 'edge' lines, this article shows you where to look, what might break when moving to Node, and why proxy.ts was never affected.

💡 Pro Tip: Deleting the runtime = 'edge' line from route/page files usually breaks nothing — because Node.js is already the default runtime; if you had to give up fs, a native module, or the full Node.js API surface because of runtime = 'edge', deleting the line brings that code back within reach.

Table of Contents

What changed, in one sentence

Vercel's official runtime documentation says: "Starting in Next.js 16.3, setting runtime = 'edge' is no longer supported. Routes and pages run on Node.js." In other words, export const runtime = 'edge' in route handlers (route.ts), pages (page.tsx), and layouts no longer has any effect and is expected to be removed. Next.js's own route-segment-config reference points the same way: the 'edge' value of the runtime option is now marked "deprecated," while 'nodejs' is the default and recommended value.

The practical implication is simple: a project that deliberately opted into the edge runtime before 16.3 now runs that code on Node.js, with identical surface behavior. Next.js's own deprecation message page summarizes the migration this way: "The Node.js runtime is the default, so no replacement is needed" — meaning the action is usually just deleting the line.

Worth noting: this change isn't called out in Next.js's 16.3 release announcement (blog.next-16-3) — the removal is documented only in Vercel's platform docs and Next.js's reference/message pages. So it's more accurate to read this not as "Next.js officially announced it," but as "Next.js's own documentation and Vercel's platform documentation were quietly updated."

Two docs seem to say two things — the real distinction (route/page vs proxy.ts)

Scanning Next.js's own documentation, you may hit a confusing contradiction. The Edge Runtime reference page (/docs/app/api-reference/edge) seems to point to Proxy as a place Edge Runtime is used. But the route-segment-config reference and the Proxy file-convention page say the opposite: "This option cannot be used in Proxy" and "Proxy defaults to using the Node.js runtime. The runtime config option is not available in Proxy files. Setting the runtime config option in Proxy will throw an error."

These two statements genuinely contradict each other — a stale, unupdated remnant of the docs, not an ambiguity in your code. The actual behavior is clear: the proxy.ts file has never allowed setting `runtime = 'edge'`, it runs on the Node.js runtime by default, and attempting that setting throws an error. runtime = 'edge' at the route/page level is a separate story: it was "deprecated but working" up to 16.3, and became officially unsupported in 16.3.

The real point of confusion here is the timeline. Proxy defaulting to the Node.js runtime and rejecting the runtime config arrived in version 16.0.0 (alongside middleware being renamed to Proxy) — not in 16.3. What changed in 16.3 isn't Proxy; it's the complete removal of runtime = 'edge' support at the route and page segment level. Not mixing up these two different versions and two different mechanisms is the single most critical point of this article.

Mechanism
Edge support
When it changed
Source
Route / Page (route.ts, page.tsx)
Deprecated-but-working up to 16.3, fully removed in 16.3
Next.js 16.3 (August 2026)
Vercel edge runtime documentation
Proxy (proxy.ts, formerly middleware.ts)
Has never accepted the runtime config since being born as Proxy, defaults to Node.js
Next.js 16.0.0 (October 2025)
Proxy file-convention reference
Edge Runtime API reference
Still carries an old statement associating it with Proxy
Not updated since February 2026 (lastUpdated 2026-02-02)
Edge Runtime API reference

Finding where runtime='edge' is used

Before starting the migration, find every file that uses the runtime = 'edge' export. This line is only valid in route handlers, pages, and layouts — proxy.ts already rejects it, so focus your search on route/page/layout files inside app/.

bash
1grep -rn "runtime.*=.*['\"]edge['\"]" app/ --include="*.ts" --include="*.tsx"

For each candidate line you find, ask three questions: (1) Is this line actually necessary, or is it a leftover copy-paste? (2) Does the code inside use fs, a native Node.js module, or an API edge doesn't support? (3) Does the route still compile after you remove it? Next.js's own deprecation message recommends the same approach: delete the line, because Node.js is already the default runtime and no other change is usually needed.

Before and after for a route file looks like this:

ts
1// Before (pre-16.3, no longer supported)
2export const runtime = "edge";
3 
4export async function GET(request: Request) {
5 return new Response("ok");
6}
ts
1// After (Node.js default, line removed entirely)
2export async function GET(request: Request) {
3 return new Response("ok");
4}

Next.js's route-segment-config reference summarizes the two values this way: 'nodejs' (default) and 'edge' (deprecated). So deleting the runtime export entirely produces the same result as explicitly writing 'nodejs' — but deleting the line is cleaner, because you won't have to ask "was I using edge?" again in the future.

Two false positives can creep in: an unrelated variable named runtimeConfig, and stale runtime references inside proxy.ts (likely dead code, since that config isn't accepted there anyway). Scoping the regex to app/ and .ts/.tsx also eliminates false matches in node_modules and .next/.

What breaks moving to Node: fs, native modules, duration limits

Edge runtime's biggest constraint was its lack of access to most Node.js APIs. Vercel's documentation describes this clearly: Edge Runtime is built on the V8 engine and exposes only a subset of Web APIs like fetch, Request, Response — the fs module, native Node.js addons, or the full Node.js API surface fall outside that subset. Moving to the Node.js runtime removes these constraints: you can access the filesystem, use native modules (e.g., image processing, cryptography libraries).

There's also a difference on duration limits. Vercel's official limit for Edge Functions is: you must start sending a response within 25 seconds, while streaming can last up to 300 seconds. When you move to the Node.js runtime, this becomes subject to Vercel's Node.js function limits and Fluid Compute's own rules — so saying "the duration limit is gone" would be wrong; the limit model changes.

On the ISR (Incremental Static Regeneration) side, Next.js's Edge Runtime reference states the situation plainly: "The Edge Runtime does not support Incremental Static Regeneration (ISR)." So a page using ISR could never run on edge in the first place; the runtime = 'edge' removal creates no behavior change for those pages.

APIs you can now use

A route handler example that doesn't work on edge but runs fine on Node.js looks like this — say, when you want to read and cache a statically generated file at build time:

ts
1import { readFile } from "node:fs/promises";
2import { join } from "node:path";
3 
4export async function GET() {
5 // node:fs is not supported in the edge runtime — you can't
6 // read from or write to the filesystem; this call works
7 // normally in the Node.js runtime.
8 const filePath = join(process.cwd(), "data", "config.json");
9 const contents = await readFile(filePath, "utf-8");
10 
11 return Response.json(JSON.parse(contents));
12}

If you'd marked a route like this with runtime = 'edge' before 16.3, it wouldn't have worked: Next.js's Edge Runtime reference says it plainly — "Native Node.js APIs are not supported. For example, you can't read or write to the filesystem." According to Vercel's documentation, Edge Runtime only provides a subset of Web APIs like fetch, Request, Response, crypto/SubtleCrypto, ReadableStream/WritableStream/TransformStream, TextEncoder/TextDecodernode:fs isn't on that list. On the Node.js runtime, this call works like a normal Node.js API.

Capability
Edge Runtime
Node.js Runtime
fetch, Request, Response
Yes
Yes
crypto.subtle (Web Crypto)
Yes
Yes
node:fs (filesystem)
No
Yes
Native Node.js addons
No
Yes
Response start-time limit
25 seconds
Subject to Vercel's Node.js function limits
Streaming upper bound
300 seconds
Subject to Fluid Compute rules

proxy.ts stays on Node, edge only in the old middleware.ts: the limits

proxy.ts (formerly middleware.ts) is Next.js's file convention for running code on the server before a request completes. The official definition: "The proxy.js|ts file is used to write Proxy and run code on the server before a request is completed... Proxy executes before routes are rendered." You can change the response through rewrites, redirects, header modification, or by producing a response directly.

But Proxy has its own officially stated limits — rules that hold regardless of the runtime = 'edge' removal:

  • Not for slow data fetching: "Proxy is not intended for slow data fetching... it should not be used as a full session management or authorization solution." Using Proxy for a heavy DB query or a slow third-party API call isn't recommended.
  • fetch cache options have no effect: fetch's cache, revalidate, and tags options have no effect inside Proxy.
  • Runs on every request without a matcher: If you don't define a matcher, Proxy runs on every request, including static files under _next/static, _next/image, and the public/ folder. If you put auth logic in Proxy and forgot the matcher, you may have accidentally routed even your CSS/JS/images through that code path.
ts
1// proxy.ts — using a matcher to avoid unnecessary runs
2export const config = {
3 matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
4};

The official upgrade guide is clear here: "The edge runtime is NOT supported in proxy. The proxy runtime is nodejs, and it cannot be configured. If you want to continue using the edge runtime, keep using middleware." It immediately follows up by noting further edge guidance is coming in a future minor release: "We will follow up on a minor release with further edge runtime instructions."

This comes at a cost: the Next.js 16 announcement explicitly declares middleware.ts's lifespan limited — "The middleware.ts file is still available for Edge runtime use cases, but it is deprecated and will be removed in a future version." So staying on the middleware file for edge is valid but not a permanent solution — it means waiting inside a deprecated file convention.

Fluid compute + Active CPU's billing impact

Vercel's Fluid Compute model has been the default for new projects since April 23, 2025, combining serverless flexibility with server-like capabilities: sharing a single instance across requests, background processing via waitUntil, automatic cold-start optimization.

The biggest difference on the billing side is the Active CPU concept: code is billed only for the milliseconds it's actually executing. While waiting on a database query or an AI model call (I/O wait time), CPU billing pauses — but Provisioned Memory billing continues during that wait. The official documentation summarizes it this way: "You are only billed during actual code execution and not during I/O operations" (for CPU) and, for memory, "Continues billing while handling requests, even during I/O operations."

There are also regional price differences — for example, in the Frankfurt (fra1) region Active CPU is $0.184/hour and Provisioned Memory is $0.0152/GB-hour, while in the Washington D.C. (iad1) region Active CPU is $0.128/hour. Moving to the Node.js runtime doesn't change this billing model; both edge and Node.js functions now run under the same Fluid Compute + Active CPU framework. Vercel recommends moving to Node.js citing "improved performance and reliability"; it doesn't publish a concrete cold-start or cost comparison.

What this means if you run your own server

If your project runs on your own VPS as a Node.js standalone server and systemd service (like this site's portfolio-ssr.service setup), this change creates no practical break. runtime = 'edge' was already a Vercel Edge Function optimization — its V8-isolated, container-free model is meaningless on your own server, since the whole process already runs in one Node.js runtime.

The one concrete effect: if your project still has a runtime = 'edge' export, you need to remove it — otherwise you'll get a deprecation warning at build time. The Fluid Compute and Active CPU billing model is entirely specific to Vercel's platform; it has no equivalent on your own server, because you've already allocated a fixed resource for a fixed process (systemd service + fixed RAM/CPU).

Bringing the migration steps together into a single checklist:

  • Search: List all candidates with grep -rn "runtime.*edge" app/.
  • Remove: Delete the export const runtime = 'edge' line from every route/page file (Node.js is already the default).
  • Build: Run next build, confirm no deprecation warning remains.
  • Keep Proxy separate: Don't add a runtime config to proxy.ts files — it's not accepted there and throws an error.
  • Check the matcher: Make sure Proxy isn't running unnecessarily on static files.
  • Test fs/native modules: Add the Node.js APIs that didn't work on edge but you now want to use (filesystem, native crypto) to your routes and test them.

GOLDEN TIP

The most valuable insight in this article

This tip holds the article's most important takeaway.

Easter Egg

You found a hidden gem!

There's a hidden detail in this section. Want to uncover it?

Reader Reward

We've gathered the steps you shouldn't skip when applying this article's migration process to your own project into a single checklist. Checking off every item lets you be confident that the runtime = 'edge' removal is safely complete on both the route/page and proxy.ts sides.

FAQ

Why doesn't runtime = 'edge' work in Next.js 16.3?

Vercel's official documentation says it directly: "Starting in Next.js 16.3, setting runtime = 'edge' is no longer supported. Routes and pages run on Node.js." Next.js's own route-segment-config reference also marks the edge value as deprecated and shows nodejs as the only recommended value. So this isn't a bug — it's the framework's deliberate decision to make Node.js the single standard runtime.

Was edge runtime removed entirely, or does it still exist in proxy.ts?

Neither is entirely accurate. proxy.ts (formerly middleware.ts) has not accepted the runtime config since Next.js 16.0.0, and runs on Node.js by default — that's a rule 16.0 brought, not 16.3. What changed in 16.3 is the removal of runtime = 'edge' support at the route and page level. The official upgrade guide also says where edge still lives: "If you want to continue using the edge runtime, keep using middleware." So edge only survives in the deprecated middleware.ts file convention. Next.js's Edge Runtime API reference page still carries an old statement associating Edge with Proxy, but that looks like an unupdated line that contradicts the official upgrade guide and the proxy file-convention page.

How do you migrate from edge runtime to the Node.js runtime?

Just delete the export const runtime = 'edge' line from route/page files entirely — no other change is usually needed since Node.js is already the default runtime. Before deleting, it's recommended to list all candidates with grep -rn "runtime.*edge" app/, then check each one for whether it needs an API edge doesn't support (fs, native modules).

What happens to cold start and cost once edge is removed?

There isn't a definitive number to answer this with right now — Next.js's 16.3 release announcement doesn't mention this at all, and Vercel's general documentation only says it recommends moving to Node.js for "improved performance and reliability," without publishing a concrete cold-start or cost comparison. The one known fact is that both edge and Node.js functions now run under the same Fluid Compute + Active CPU billing model — Active CPU only charges for the time code is actually running; CPU billing pauses during I/O wait, but memory billing continues.

What happens if I write runtime = 'edge' in proxy.ts?

You get an error. The Proxy file-convention reference is clear: "The runtime config option is not available in Proxy files. Setting the runtime config option in Proxy will throw an error." This is different from the deprecation warning on routes/pages — trying this setting in Proxy directly results in a build/runtime error.

Conclusion

Next.js 16.3's runtime = 'edge' removal is, for most codebases, a one-line cleanup: deleting this export from routes and pages usually breaks nothing else, since Node.js is already the default. The point that actually deserves your attention is that this is an entirely separate change from proxy.ts's behavior (already tied to Node.js since 16.0) — two different versions, two different mechanisms.

If you're more interested in the edge/serverless ecosystem on the backend side, the Supabase Edge Functions and Deno runtime piece shows how an edge-native alternative works; the Drizzle ORM + Turso edge SQLite pattern covers database access suited to edge runtime constraints. If you want to compare serverless frameworks, the Hono.js production guide is a useful reference. If you want to add this kind of deprecation check to your CI/CD pipeline, the pipeline logic in iOS CI/CD Pipeline: GitHub Actions and Fastlane can be adapted platform-independently. For what to watch for when building a production-ready API layer, the Network Layer Optimization guide shares the same discipline as this article: proceed from sources, not assumptions.

Sources

Tags

#Next.js#Edge Runtime#Node.js#Vercel#proxy#Fluid Compute#serverless
Muhittin Çamdalı

Muhittin Çamdalı

Lead Mobile Engineer

Lead Mobile Engineer with 12+ years of experience. Expert in iOS, Android and cross-platform architectures with Swift, SwiftUI, Kotlin and Flutter. I build performant, user-friendly mobile apps.

iOS Development News

Weekly Swift tips, SwiftUI tricks and iOS best practices. No spam, only valuable content.

We respect your privacy. You can unsubscribe at any time.

Share