Edge functions run your code at the CDN point of presence closest to the user rather than in one cloud region; regional serverless functions (AWS Lambda, Vercel's Node.js functions) run in a single region with a full Node.js environment. The question that decides where rendering should happen is simple: does your code need to be close to the user, or close to the data? As of July 2026, the honest answer is usually both, split across two runtimes.
This guide covers what separates edge from regional serverless, where the latency win is real versus marketing, the classic "data-locality trap," streaming SSR at the edge, and the runtime constraints you actually hit in production — ending with a table to help you decide what runs where.
What is an edge function, and how is it different from regional serverless?
An edge function runs in a lightweight runtime, typically V8 isolates, deployed simultaneously across dozens of points of presence worldwide; whichever city a request comes from, it executes at the nearest one. A regional serverless function (classic AWS Lambda or Vercel's Node.js runtime) lives in a single region with full access to the Node.js API surface and npm packages, but every request has to travel to that one region.
In practice the difference comes down to this: an edge function shortens network distance, a regional function preserves proximity to your database. Both are "serverless," but they optimize for different things. Vercel's Edge Runtime documentation is explicit about this: in production the Edge Runtime uses V8, not Node.js, so you don't get the full Node.js API — only a subset of Web APIs (fetch, Request/Response, Web Crypto, ReadableStream).
One notable 2026 detail: Vercel retired its standalone "Edge Functions" product in June 2025 and folded the edge runtime into Vercel Functions under its fluid compute model. The choice is now per function, not per product — you set runtime: 'edge' or 'nodejs' on each route rather than deploying to a separate offering.
// app/api/greet/route.ts — edge runtime, low latency, narrow API surface
export const runtime = 'edge';
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const name = searchParams.get('name') ?? 'world';
return new Response(`Hello, ${name}!`, {
headers: { 'content-type': 'text/plain' },
});
}// app/api/report/route.ts — Node.js runtime, needs native modules and file system access
export const runtime = 'nodejs'; // default; required for fs, sharp, native bindings
export async function POST(request: Request) {
const body = await request.json();
const pdf = await generatePdfWithNativeLib(body); // native dependency, won't run on the edge
return new Response(pdf, { headers: { 'content-type': 'application/pdf' } });
}The same split shows up in Next.js's Route Segment Config documentation: runtime defaults to 'nodejs' unless you explicitly opt into 'edge'.
Is the latency win real? Where does it stop?
Yes, it is real — but only for network distance. A single-region setup can put 100–300ms of network round-trip between a user and the server; a function running at the nearest edge point cuts that to roughly 10–50ms. Cloudflare Workers' V8 isolate model also pushes cold starts close to zero, versus 100ms–1s for a container-based serverless cold start.
But that win applies only to the function's own execution time and network distance. If the function still calls a database, a third-party API, or a single-region origin, that call has to travel just as far as before. Edge moves you closer to the user — it does not move you closer to your data. Missing that distinction is exactly how teams fall into the trap covered next.
The data-locality trap
The data-locality trap is this: you move the compute to the edge, but your database or origin is still sitting in one region, so you don't eliminate the round trip — you just relocate it. An edge function in Singapore querying a single-region PostgreSQL instance in US East waits roughly 200ms per query, which is slower overall than a regional serverless function co-located with that same database at single-digit-millisecond latency. Even if the function itself executes in 2ms, the database round trip dominates the total response time.
Fair warning: a meaningful share of the "we moved it to the edge" announcements in 2026 look more like a marketing checkbox than a measured latency win. If your database is single-region and every request still round-trips to it, moving the function to the edge looks good on a diagram but rarely changes what the user actually experiences.
There are real ways to fix this: replicate the database across regions (read replicas), use edge-compatible data layers (Cloudflare D1/KV, Vercel Edge Config), or cache frequently read data at the edge. But moving the function alone, without touching the data layer, hides the problem instead of solving it.
Streaming SSR at the edge
The edge runtime pairs well with React's streaming SSR because both are built on the same Web Streams API. The server can send the HTML that's ready immediately and stream in the parts that depend on slow data behind a Suspense boundary:
// app/product/[id]/page.tsx — streaming SSR at the edge
export const runtime = 'edge';
import { Suspense } from 'react';
export default function ProductPage({ params }: { params: { id: string } }) {
return (
<main>
<h1>Product details</h1>
<Suspense fallback={<p>Loading price…</p>}>
{/* @ts-expect-error async server component */}
<PriceFromDb id={params.id} />
</Suspense>
</main>
);
}The data-locality trap still applies here: if PriceFromDb hits a remote, single-region database, streaming only lets the user see the shell sooner — it doesn't make the slow query faster. Streaming is a presentation technique that improves perceived performance; it is not an architectural fix for a data round trip. For a broader comparison of rendering approaches, see our SSR vs SSG vs ISR guide; to understand which pieces of your UI actually stay on the server in the first place, our React Server Components in Next.js 15 piece is a good starting point.
Runtime constraints you'll actually hit
The edge runtime is a trimmed-down subset of Node.js, and three constraints cause the most production friction:
- No native modules. Packages that depend on
fs,child_process, or native bindings (some image-processing or PDF libraries, for instance) don't run on the edge; they need the Node.js runtime. - Smaller bundle-size caps. Per Cloudflare Workers documentation, the free plan caps compressed code at 3MB and the paid plan at 10MB, and the global scope (top-level code) has to finish executing within a short, fixed window. Vercel's edge runtime carries similar size and API restrictions.
- A limited API surface. Instead of full Node.js compatibility, you get a Web API subset, and several popular npm packages — especially ones that do Node-specific I/O — either don't run on the edge at all or need edge-compatible replacements.
The practical rule follows from that: light, data-light work — auth checks, redirects, A/B testing, simple personalization — belongs on the edge; anything with native dependencies, heavy computation, or a hard dependency on a single-region database belongs on the Node.js runtime.
Decision table: where should this render, actually?
Dimension | Edge | Origin (regional serverless) | Client |
|---|---|---|---|
Network latency to user | Lowest (~10–50ms) | Moderate-to-high (100–300ms, region-dependent) | None (runs locally) |
Data proximity | Low — database is usually remote | High — usually co-located in the same region | None, every fetch travels the network |
Cold start | Near-zero (V8 isolate) | Low-to-moderate (100ms–1s, config-dependent) | None |
Runtime constraints | No native modules, narrow API, small bundle cap | Full Node.js, native modules, large bundles allowed | Browser APIs only |
Best fit | Auth checks, redirects, simple personalization, static/streaming shell | Database-heavy work, native dependencies, complex computation | Instant interaction, form validation, animation |
Warning: the single most important takeaway from this table is that edge compute close to the user does not improve total latency if your database still lives in one region — it just relocates where the round trip happens. Look at where your data layer actually sits before you decide where the function should run.
Edge-versus-origin awareness is now considered a core frontend and full-stack skill in 2026, and "server-first" — server components and server rendering by default — has become the mainstream default in this year's frameworks. For the broader performance picture, see our Core Web Vitals checklist; for content-heavy sites weighing rendering strategy up front, see our Astro vs Next.js comparison; and browse more coverage in our Web Development category.
Frequently Asked Questions
Is an edge function always faster than a regional serverless function?
No. It's only faster for network distance and cold start. If the function depends on a single-region database or API, that round trip dominates total response time and can erase the edge's network advantage entirely.
Does streaming SSR solve the data-locality trap?
No, it only improves perceived performance. The user sees the ready part of the HTML sooner, but the part waiting on slow data still pays the same round trip; streaming hides the delay rather than removing it.
What kinds of code can't run on the edge?
Node.js-specific modules like fs and child_process, packages that need native bindings, and dependencies too large for the runtime's bundle cap (Cloudflare Workers caps compressed code at 3–10MB depending on plan) all require the Node.js runtime instead.
Which runtime should a small project start with?
For most projects, starting on the Node.js runtime and moving only the specific routes with a measured latency problem — auth, redirects, simple personalization — to the edge is safer than deploying everything to the edge up front.



