Why I Moved My Next.js Functions from Edge to Node.js
September 24, 2026 Avishka Devinda
September 24, 2026 Avishka Devinda
For a long time, putting a function on the Edge sounded like an automatic performance upgrade.
Run code closer to users, reduce startup time, and make requests faster.
I used the Edge runtime in my own Next.js project for things like an OG image route and email endpoints.
After upgrading the project to Next.js 16, I changed those functions back to Node.js.
Here is why.
This is the first thing that confused the discussion.
Using the Node.js runtime does not mean the application stops using Vercel's global network.
Static assets, CDN caching, routing, and delivery can still happen through Vercel's infrastructure.
The runtime only decides where and how the server-side JavaScript function executes.
That means I can use Node.js for the function while still getting the benefits of a modern CDN in front of the application.
When I built the project with Next.js 16, the build printed this warning:
The Edge Runtime is deprecated.
You can use the "nodejs" runtime instead.
That was a strong reason to review every route where I had explicitly configured:
export const runtime = 'edge';
I did not want to keep a runtime only because it had been a good choice several versions ago.
The contact form used Resend and a React email template.
On Edge, the request eventually timed out before Resend even received the email API call.
That was the important clue.
The problem was not email delivery.
The function was failing before it reached the provider.
For an email endpoint, Node.js gives the dependencies the environment they expect and removes one more compatibility variable.
So I changed it to:
export const runtime = 'nodejs';
The Edge runtime is intentionally smaller than a full Node.js environment.
That can be useful, but it also means packages that expect Node APIs may not work exactly the same way.
Most of the packages in my application are built and tested heavily around Node.js.
If I do not have a specific reason to choose another runtime, Node.js is the safer default.
My OG route used next/og, custom fonts, and image generation.
It worked on Edge, but keeping Edge meant carrying the deprecation warning and Edge-specific bundle constraints.
I had already spent time optimizing font placement to keep the Edge bundle small.
Moving the route to Node.js made the architecture simpler.
Now the application does not have one special runtime only for generating an image.
A function being geographically close to the browser does not automatically mean the complete request is faster.
Consider an API request that does this:
User
↓
Function
↓
Database or external API
↓
Function
↓
User
If the database or API is in one region, that network call may dominate the total latency.
For my contact form, the important network request is the call to the email provider.
For a database-heavy route, being close to the database can matter more than being close to the browser.
I still like globally distributed compute for workloads that genuinely benefit from it.
Examples can include:
But I no longer choose Edge just because it sounds faster.
I choose the runtime based on the actual workload.
My current default is:
Normal server logic → Node.js
Database operations → Node.js
Email / external SDKs → Node.js
OG image generation → Node.js
Special global workload → evaluate separately
This gives me one predictable runtime for most of the application.
Moving from Edge back to Node.js felt strange at first because Edge had been positioned as the faster modern option.
But simpler infrastructure is also a performance feature.
If Node.js removes compatibility problems, build warnings, special bundle limits, and runtime differences, that can be a better engineering choice than optimizing for theoretical latency.
For my Next.js project, Node.js is now the default until I have a specific workload that proves it needs something else.