Lesson 7 of 41
Edge Rendering — the Honest Trade-offs
Rendering at the CDN edge: what it buys (latency, geographic proximity), what it costs (database distance, runtime limits), and when it's the right call vs. regional SSR.
Previous lessons in this track covered the rendering strategies — CSR, SSR, SSG, ISR, streaming SSR, Server Components — as timing choices. Edge rendering is a different axis: it's about where the server-side part physically runs. All the other strategies work either way; moving to the edge changes latency, runtime constraints, and how you think about data.
This lesson is about the honest trade-offs. It's an easy place to be over-sold by marketing (the "edge is always faster" line) and a hard place to work backwards from if you commit without understanding what you've signed up for.
What "the edge" actually means
CDN providers (Cloudflare, Vercel, Fastly, AWS) operate networks of points of presence — data centers physically distributed around the world, usually dozens to hundreds of locations. A user in Tokyo hits the Tokyo PoP; a user in São Paulo hits the São Paulo one. This network has always been there for static assets: the CSS and JS files were cached at every PoP so they loaded fast regardless of where the origin server was.
Edge rendering extends that network to run your rendering code. When a user requests a page, the render executes at the nearest PoP — often under 50ms away from the user — instead of in a single regional datacenter that might be thousands of miles away.
- Step 1
Regional SSR
User in Tokyo → request crosses Pacific → render runs in us-east-1 → response crosses Pacific back. Every page load pays two trans-oceanic hops of latency.
- Step 2
Edge rendering
User in Tokyo → request terminates in Tokyo PoP → render runs there → response is already at the user. Trans-oceanic latency only shows up if the render needs data that lives far away.
The win, when it materialises, is real: a cold SSR response with TTFB around 30–50ms instead of 200–400ms is a meaningfully different user experience.
The data distance problem
The thing the marketing gets quiet about: moving the rendering code to the user doesn't move the user's data to the user. If the render in Tokyo needs to query a database in us-east-1, every query crosses the Pacific.
// Edge-rendered page, naively ported from regional SSR
export default async function Dashboard({ userId }) {
const user = await db.user.findUnique({ where: { id: userId } }); // 150ms
const posts = await db.post.findMany({ where: { userId } }); // 150ms
const notifications = await db.notification.list({ userId }); // 150ms
return <Layout>{/* ... */}</Layout>;
}Three sequential queries from Tokyo to Virginia is ~450ms of pure network wait, before any rendering even starts. A regional SSR in us-east-1 would have done those same queries over the local network in a handful of milliseconds. The edge version is dramatically slower despite being physically closer to the user.
This is why edge rendering is a system design, not a single switch. For it to actually win, the data has to be close to the edge too.
Making edge rendering win: data locality
Four patterns get you back into the "edge is actually faster" regime:
- A globally-distributed data layer. Cloudflare D1, Turso, Neon with read replicas, FaunaDB — databases designed to be queried from anywhere with sub-100ms latency. The architecture you'd use for anything multi-region.
- Edge KV stores. Cloudflare Workers KV, Vercel Edge Config, Upstash Redis — key-value stores at the PoP, with eventual consistency. Use for configuration data, feature flags, session tokens, cached user profiles.
- Aggressive caching with stale-while-revalidate. If the render's data doesn't need to be fresh-to-the-millisecond, cache the data layer's response at the edge. First request pays the far-away hop; subsequent requests don't.
- Parallel data fetching. If the render must make multiple
far-away queries, at least fire them in parallel.
Promise.allturns three 150ms sequential queries into one 150ms wait.
The honest sentence: edge rendering wins when the render's data dependencies are either close to the edge or cached there. When they're not, regional SSR is the better call and it's not close.
Runtime constraints worth planning for
Edge runtimes are not Node. They're built on V8 isolates and
the standard Web APIs — fetch, crypto.subtle, Web Streams,
URL, TextEncoder. Node APIs (fs, path, child_process,
native modules) are either missing or only partially polyfilled.
Practical consequences:
- Database drivers. The TCP-based Postgres driver you've used for years probably doesn't work at the edge. Reach for HTTP- based drivers (Neon's HTTP driver, PlanetScale's serverless driver), or a database service that provides one.
- File system reads. No filesystem. Files have to be imported at build time (bundled into the worker) or fetched from a blob store.
- Native crypto / native image processing.
sharp,argon2and friends either don't run or need WASM alternatives. - CPU time limits. Most edge platforms cap per-request CPU time in the tens-of-milliseconds range (wall-clock limits are higher — tens of seconds — because they include waiting on I/O). A compute-heavy render belongs on a regular server, not at the edge.
- Memory limits. 128MB is common. Loading a large library or processing a large payload may not fit.
Most frameworks (Next.js, Remix) ship a bundle analyzer that flags non-edge-safe imports; still, it's the kind of thing that bites halfway through a migration. Plan for the ecosystem tax.
When edge rendering is the right call
Three shapes where it genuinely wins:
- Data-light pages served globally. Marketing pages, documentation, blog posts, landing pages. The data is either baked in at build time or lives in an edge KV. The render is cheap. Global TTFB under 50ms is achievable.
- Personalisation at the edge. A/B tests, feature flags, geo-based content variants. The data (flags, variants) lives in an edge KV; the "render a variant" logic runs at the PoP; the user gets a personalised response at edge-latency speeds.
- Request-time filtering / rewriting. Edge middleware that rewrites URLs, redirects based on country, blocks bots, or attaches auth headers. This is the use case edge platforms were originally built for and the one they're best at.
Three shapes where it doesn't:
- Data-heavy dashboards whose primary database lives in a single region and whose latency budget is already tight.
- Long-running computes — image processing, PDF generation, LLM inference beyond a short prompt. The CPU limits bite.
- Apps with heavy Node ecosystem dependencies — anything reliant on native modules or on a Node-only library whose maintainer hasn't updated it.
The practical rule: check your p95 TTFB for your current regional SSR, then calculate what the edge version would be given your real data layer. If the calculation doesn't reliably beat the regional TTFB, don't migrate.
The hybrid pattern most mature systems land on
The honest common endpoint for mature systems is a split:
- Edge runs middleware (auth checks, redirects, geo rewrites, A/B test variant selection), static pages, and data-light personalised pages.
- Region runs data-heavy pages, mutation handlers, long- running computes, and anything that needs a Node-only dependency.
- Server Components on the regional side can still feed edge-rendered pages as "pre-rendered islands" via ISR.
This is a design that acknowledges the actual trade-off: edge is about proximity, region is about data access and runtime headroom. Pick the right one per route, not per app.
What to remember
- Edge rendering runs your server-side rendering code on the CDN provider's global PoP network, so the render is physically close to the user. TTFB wins of 100–300ms are real — when the rest of the system is designed for it.
- The data distance problem is the trap: the code moves, but the database doesn't. Three sequential queries from a far-away database cost the same latency per query regardless of where the render runs.
- Make edge win by making the data close to the edge too: distributed databases, edge KV stores, aggressive cache + SWR, parallel fetches.
- Edge runtimes are V8 isolates, not Node. Expect missing Node APIs, HTTP-based database drivers, no filesystem, tight CPU/memory limits. Plan for the ecosystem tax.
- Edge is right for data-light global pages, edge-personalisation / A-B, and request-time middleware. Edge is wrong for data-heavy regional dashboards, long-running computes, and Node-dependent code.
- Mature systems split: edge for middleware and static-ish pages, region for data-heavy and compute-heavy work. Pick per route, not per app.
Check yourself
4 questions · pass 3/4 to unlock Auth Boundaries in a Rendered App
1.What does 'edge rendering' actually mean in the sense modern frameworks use the term?
2.A naive edge-rendered page needs user data from a database that lives in us-east-1. The user is in Tokyo. What tends to happen to TTFB, and why?
3.Which of these is a realistic design pattern that makes edge rendering actually win on a data-heavy page?
4.What's a concrete runtime constraint of an edge runtime (Cloudflare Workers / Vercel Edge Functions) compared to a full Node.js server, and why does it matter?
4 left to answer
Discussion
Sign in to postNo comments yet. Be the first to say something.