Personalization on Vercel Edge: Edge Config + Middleware Patterns
Vercel Edge Config + middleware lets you personalize Next.js pages at the edge in sub-20ms. The patterns that actually work in production.
Personalization on Vercel Edge: Edge Config + Middleware patterns
TL;DR.
- Personalization on Vercel Edge works by reading a small global key-value store (Edge Config) from a middleware that runs before the cache and rewriting the request to a pre-rendered variant.
- Edge Config reads complete within
15 msat P99 per Vercel's own docs, and often under1 ms. That is the budget personalization on Vercel Edge has to compete with.- The store is small by design:
8 KBon Hobby,64 KBon Pro,512 KBon Enterprise. It is feature flags and segment definitions, not your user database.- Routing Middleware is the right tool for variant selection and routing. Vercel Functions are the right tool for personalized payloads that need a database. Don't mix them up.
- ISR + middleware compose cleanly: middleware decides which statically generated page to serve. The page itself stays cached at the edge.
If you are building personalization on Vercel Edge in 2026, the architecture you actually want is small, boring, and fast. A middleware reads a feature-flag-shaped store, decides which variant a user belongs in, and rewrites the URL to a pre-rendered page. No origin SSR. No client-side flicker. No third-party JavaScript on the critical path. This post walks through the patterns we use, where Edge Config fits, and where it breaks down.
Why personalization on Vercel Edge looks the way it does
The classic "personalization stack" is a single rendered page that calls out to a feature-flag service and a profile API on every request, then re-renders. It works. It is also slow, expensive, and reliably flickers when you load a script tag late.
Vercel's Edge model inverts that. Instead of rendering one page slowly, you pre-render N variants and decide which variant to serve in middleware. Per Vercel's own Routing Middleware docs, middleware "executes code before a request is processed on a site" and runs globally before the cache. That makes it a natural seam for personalization on Vercel Edge: the middleware is the only piece that has to be dynamic, and everything downstream of it can stay cached.
The mental model worth keeping: middleware is where which gets decided. Pages are where what gets rendered. Edge Config is where who-belongs-where lives. If you find yourself fetching user profiles from middleware on every request, you have wandered out of the pattern.
Edge Config as the read-side state store
Edge Config is Vercel's global key-value store optimized for reads. The Edge Config docs state that "the vast majority of your reads will complete within 15 ms at P99, or often less than 1 ms." That is the kind of latency a middleware can afford to spend per request without blowing your performance budget.
Two properties make Edge Config fit personalization specifically:
- Reads are pushed, not pulled. Vercel's network replicates the entire store to every region. The middleware does not make a network call to a single origin database; it reads a local copy. That is how you get sub-millisecond reads.
- Writes are eventually consistent. Per Vercel's Edge Config limits, updates take "up to
10 secondsfor the update to be globally propagated." If you need to flip a feature flag for a beta cohort and have it take effect exactly now, Edge Config is not the right tool. If you can wait10 s, it is great.
What goes in Edge Config? Segment definitions ("users in cohort power_user see variant B"), feature flag config, redirect tables, country-to-locale maps, allow/block lists. What does not belong in Edge Config: per-user profiles, recommendation results, anything that grows with your user count.
Size limits force good hygiene
The size limits are the most important constraint, and they are not subtle:
| Plan | Max store size | Max stores | Max stores per project |
| --- | --- | --- | --- |
| Hobby | 8 KB | 1 | 1 |
| Pro | 64 KB | 3 | 3 |
| Enterprise | 512 KB | 10 | 3 |
These come straight from Vercel's Edge Config limits page. The 512 KB cap on Enterprise is the upper bound; you can request more via Vercel Support, but you should not. If your personalization config does not fit in 512 KB, it is not config anymore — it is a database, and you should treat it as one.
In practice we have found 64 KB is plenty for: a few dozen flags, a few hundred segment definitions, a few thousand redirect rules. If you exceed that, the culprit is almost always per-user data leaking into config. Pull it back out.
Vercel middleware personalization: the routing pattern
The middleware pattern for personalization on Vercel Edge has three steps: identify the user, look up their segment, rewrite to the variant. Identification is usually a cookie. Lookup hits Edge Config. The rewrite returns the right pre-rendered page.
// middleware.ts
import { get } from '@vercel/edge-config';
export default async function middleware(request: Request) {
const url = new URL(request.url);
if (url.pathname !== '/') return;
const cookieHeader = request.headers.get('cookie') ?? '';
const segment = cookieHeader.match(/marble_seg=([^;]+)/)?.[1] ?? 'control';
// Pull the variant map for the home route from Edge Config.
// One read for the whole map > one read per key.
const variants = (await get<Record<string, string>>('home_variants')) ?? {};
const variant = variants[segment] ?? 'control';
url.pathname = `/_variants/home/${variant}`;
return Response.rewrite(url);
}
Three details to notice. First, the middleware reads the whole variant map in one call. The Edge Config docs are explicit: "use getAll() instead of separate get(key) calls" so they count as a single read. Same logic for any structured key — fewer reads, lower bill, same latency. Second, the rewrite target is an internal path that maps to a pre-rendered page. The user never sees /_variants/home/B in their URL bar. Third, no async work happens after the rewrite. Whatever logic you put after the response returns will not run; Vercel cancels it.
Cookie-based identification
For new visitors with no segment cookie, we set one in the middleware response. The assignment can be deterministic (hash of the visitor ID into N buckets) or pulled from Edge Config (a segment definition document). For the Marble product, we deterministically hash a visitor_id cookie into 1000 buckets, then look up which segment owns which bucket range. That gives us reproducible assignments without writing anything per user.
Geo + device variants
Vercel's middleware exposes geo and user-agent data on the request object. For routing-by-country or routing-by-device, you do not even need Edge Config — you can branch directly:
import { geolocation } from '@vercel/functions';
export default function middleware(request: Request) {
const { country } = geolocation(request);
const url = new URL(request.url);
if (country === 'DE' && url.pathname === '/') {
url.pathname = '/de';
return Response.rewrite(url);
}
}
The trap to avoid: do not push every geo rule into code. As soon as your marketing team wants to flip the German variant on without a redeploy, you want the country-to-route map in Edge Config and the middleware reading it.
Middleware vs Vercel Functions: where the line is
Both run on Vercel's edge or Node runtime. Both can read Edge Config. The distinction is purpose, not capability.
Use Routing Middleware when the output of the request depends on which page to serve. Variant routing, locale routing, redirects, auth gates that send users to /login. Middleware can rewrite, redirect, and add headers. It cannot return a rich response body efficiently — that is the page's job.
Use a Vercel Function when the output depends on what content to render. Personalized product grids, recommendation feeds, anything that needs a database read scoped to the user. The function can run on the Edge runtime if its only dependency is Edge Config; it has to run on Node.js if you are talking to a Postgres or vector database with a regional connection. Per the Routing Middleware docs, the default runtime for middleware is Edge, but you can opt into Node.js or Bun via the config export.
The pattern we recommend: middleware picks the page, the page renders the chrome, and an embedded function call (RSC, route handler, or a client-side fetch to an Edge Function) fills in the personalized slot. That keeps middleware fast and the personalized payload out of the cache.
Middleware request limits
Worth memorizing, from the Routing Middleware docs:
- Max URL length:
14 KB - Max request body:
4 MB - Max request headers: 64, total length
16 KB
These are generous for normal traffic. They become real if you are passing large encrypted segment payloads in cookies — keep cookies under 4 KB per cookie and you will not hit anything.
ISR + personalization, composed
The trick that makes personalization on Vercel Edge cheap is that Incremental Static Regeneration and middleware compose. ISR pre-renders pages and caches them at the edge with a stale-while-revalidate policy. Middleware decides which cached page to serve. Neither knows about the other.
In practice: you have a homepage with three variants, A, B, and C. You author them as /_variants/home/A, /_variants/home/B, /_variants/home/C, each marked for ISR with a 60-second revalidation. The middleware on / reads the user's segment from a cookie, hits Edge Config for the segment-to-variant map, and rewrites to the right variant path. The page itself is served from the edge cache. Total origin compute per request: zero, until ISR revalidates.
This is the dynamic-at-the-speed-of-static promise. The cost is content authoring: someone has to maintain three variants. The benefit is performance: every request gets a cached HTML payload, no SSR latency.
For deeper coverage of the read/write split this implies, see our reference architecture for real-time personalization, which walks through the same pattern at a system level.
When Edge Config is the wrong choice
We have spent the post telling you to use Edge Config. Here is when not to:
- Per-user state. Recommendations, viewed-products, last-session. Edge Config is global and small; user state is per-user and large. Use a database — KV, Redis, Postgres — and read it from a function.
- Sub-second freshness. Writes propagate in "up to
10 seconds" globally. If you flip a flag and need it live in500 ms, this is not the tool. Use feature-flag-as-a-service or a KV store with a faster propagation guarantee. - High write volume. Edge Config bills per write at
$5.00on Pro per the limits page, and is optimized for "data that is accessed frequently and updated infrequently." If your config changes more than a few times per minute, you are using the wrong primitive.
The cleanest mental check: is this config or data? Config is small, slow-changing, and read on every request. Data is large, fast-changing, and read for specific users. Edge Config is for config.
How ×marble fits in
Vercel Edge gets you the infrastructure for personalization on Vercel Edge. It does not get you the recommendations — what variant should this user see, what products belong on their grid, what content matches their intent.
That is what we built ×marble for. It is a personalization knowledge graph that runs as a service: you send user events and product data, and it returns segment assignments, recommendations, and ranking signals that you can drop into Edge Config (for segment definitions) or call from a Vercel Function (for per-user payloads). If you are already on Vercel, the integration is two pieces: a webhook from ×marble that updates Edge Config when segments change, and a server-side SDK call from your page for personalized content. We use the same pattern in our own products Vivo, Video, and Marble × Music, and we ship the SDK that lets you reproduce it. The home is at timesmarble.com.
FAQ
How fast is personalization on Vercel Edge?
Per Vercel's Edge Config docs, reads complete in under 15 ms at P99 and often under 1 ms. The middleware itself adds a small amount of compute time on top. End-to-end, a well-built personalization on Vercel Edge setup runs comfortably under 30 ms of overhead on top of the cached page.
Can I use Vercel Edge Config for A/B testing?
Yes — that is one of its canonical use cases. The Edge Config landing page lists "experimentation with feature flags, A/B testing, critical redirects, and IP blocking" as primary use cases. The pattern is: store variant assignments or weights in Edge Config, read them from middleware, rewrite to the assigned variant.
What is the size limit of Vercel Edge Config?
The store size cap is 8 KB on the Hobby plan, 64 KB on Pro, and 512 KB on Enterprise. Enterprise customers can request higher limits via Vercel Support, but the recommendation is to use fewer larger stores and keep per-user data out.
Should I use middleware or an edge function for personalization?
Use Routing Middleware when the decision is which page to serve: variant routing, redirects, geo-routing, auth gates. Use a Vercel Function (Edge or Node runtime) when the decision is what content to render: per-user recommendations, personalized feeds, anything that depends on a database read. Middleware is the routing layer; functions are the rendering layer.
Does Edge Config replace LaunchDarkly or Statsig?
For simple flags and segment routing, yes. For complex flag rules, percentage rollouts with targeting, and experimentation analytics, no — and you do not have to choose. Vercel's feature flag integrations sync LaunchDarkly and Statsig flag definitions into Edge Config, so you get the vendor's authoring UX with edge read latency.
Further reading
- Reference architecture for real-time personalization — system-level view of the read/write split this post implies.
- Five patterns for adding personalization to your app — which patterns map to middleware vs functions.
- The marketing engineer's personalization stack — where Edge Config sits in a broader stack.
- Recommendation engine vs personalization layer — what Edge Config is and isn't.
- Vercel Edge Config docs — primary source for limits and latency claims.
- Vercel Routing Middleware docs — primary source for middleware runtime and request limits.
×marble is the personalization graph.
One API. A living knowledge graph per user. Day-zero ready, explainable by construction. We built it so you don't have to.