Precompute
Precompute
enables you to use feature flags on static pages.
Precomputing describes a pattern where Edge Middleware uses feature flags to decide which variant of a page to show. This allows you to keep the page itself static, which leads to incredibly low latency globally as the page can be served by a CDN or Edge Network.

Manual approach
In its most simple form this pattern is implemented creating two
versions of the home page in app/home-a/page.tsx
and
app/home-b/page.tsx
. Then, use Edge Middleare to rewrite
the request either to /home-a
or /home-b
.
import { flag } from '@vercel/flags/next';
export const homeFlag = flag<boolean>({ key: 'home', decide: () => Math.random() > 0.5,});
import { NextResponse, type NextRequest } from 'next/server';import { homeFlag } from './flags.ts';
export const config = { matcher: ['/'] };
export async function middleware(request: NextRequest) { const home = await homeFlag();
// Determine which version to show based on the feature flag const version = home ? '/home-b' : '/home-a';
// Rewrite the request to the appropriate version const nextUrl = new URL(version, request.url); return NextResponse.rewrite(nextUrl);}
This approach works well for simple cases, but has a few downsides:
- It can be cumbersome having to maintain both
/home-a
or/home-b
. - The approach doesn't scale well when a feature flag is used on more than one page, or when multiple feature flags are used on a single page.
Use the precompute functionality of the Flags SDK to work around the limitations of the manual approach.
Precomputing
This is an extension to the previously described pattern. It allows combining multiple feature flags on a single, static page.
This pattern is useful for experimentation on static pages, as it allows middleware to make routing decisions, while being able to keep the different variants of the underlying flags static.
It further allows generating a page for each combination of feature flags either at build time or lazily the first time it is accessed. It can then be cached using ISR so it does not need to be regenerated.
Technically this works by using dynamic route segments to transport an encoded version of the feature flags computed within Edge Middleware. Encoding the values within the URL allows the page itself to access the precomputed values, and also ensures there is a unique URL for each combination of feature flags on a page. Because the system works using rewrites, the visitor will never see the URL containing the flags. They will only see the clean, original URL.
Export flags to be precomputed
You can export one or multiple arrays of flags to be precomputed. This by itself does not do anything yet, but you will use the exported array in the next step:
import { flag } from '@vercel/flags/next';
export const showSummerSale = flag({ key: 'summer-sale', decide: () => false,});
export const showBanner = flag({ key: 'banner', decide: () => false,});
// a group of feature flags to be precomputedexport const marketingFlags = [showSummerSale, showBanner] as const;
Precompute flags in middleware
In this step, import marketingFlags
from the flags
file
that you created in the previous step. Then, call
precompute
with the list of flags to be precomputed.
You'll then forward the precomputation result to the underlying
page using an URL rewrite:
import { type NextRequest, NextResponse } from 'next/server';import { precompute } from '@vercel/flags/next';import { marketingFlags } from './flags';
// Note that we're running this middleware for / only, but// you could extend it to further pages you're experimenting onexport const config = { matcher: ['/'] };
export async function middleware(request: NextRequest) { // precompute returns a string encoding each flag's returned value const code = await precompute(marketingFlags);
// rewrites the request to include the precomputed code for this flag combination const nextUrl = new URL( `/${code}${request.nextUrl.pathname}${request.nextUrl.search}`, request.url, );
return NextResponse.rewrite(nextUrl, { request });}
Accessing the precomputation result from a page
Next, import the feature flags you created earlier, such as
showBanner
, while providing the code from the URL and the
marketingFlags
list of flags used in the precomputation.
When the showBanner
flag is called within this component it
reads the result from the precomputation, and it does not invoke the
flag's decide
function again:
import { marketingFlags, showSummerSale, showBanner } from '../../flags';type Params = Promise<{ code: string }>;
export default async function Page({ params }: { params: Params }) { const { code } = await params; // access the precomputed result by passing params.code and the group of // flags used during precomputation of this route segment const summerSale = await showSummerSale(code, marketingFlags); const banner = await showBanner(code, marketingFlags);
return ( <div> {banner ? <p>welcome</p> : null}
{summerSale ? ( <p>summer sale live now</p> ) : ( <p>summer sale starting soon</p> )} </div> );}
This approach allows middleware to decide the value of feature flags and to pass the precomputation result down to the page. This approach also works with API Routes.
Enabling ISR (optional)
So far you've set up middleware to decide the value of each feature flag to be precomputed and to pass the value down. In this step you can enable ISR to cache generated pages after their initial render:
import type { ReactNode } from 'react';
export async function generateStaticParams() { // returning an empty array is enough to enable ISR return [];}
export default async function Layout({ children }: { children: ReactNode }) { return children;}
In the example above we used generateStaticParams
on the
layout. You can also specify it on the page instead. It depends on
whether a single page needs the flag or all pages within that layout
need the flag. You have full control here.
Opting into build-time rendering (optional)
The @vercel/flags/next
submodule exposes a helper function
for generating pages for different combinations of flags at build time.
This function is called generatePermutations
and takes a
list of flags and returns an array of strings representing each
combination of flags:
import type { ReactNode } from 'react';import { generatePermutations } from '@vercel/flags/next';
export async function generateStaticParams() { const codes = await generatePermutations(marketingFlags); return codes.map((code) => ({ code }));}
export default function Page() { /* ... */}
You can further customize which specific combinations you want render by
passing a filter function as the second argument of
generatePermutations
. And just like in the example above,
you can also control whether you specify these permutations on the
individual pages or on a layout.
Declare available options
Options are the possible values that a flag can take. You can declare the available options for a flag by passing an options
array to the flag
function:
export const greetingFlag = flag<string>({ key: 'greeting', options: ['Hello world', 'Hi', 'Hola'], decide: () => "Hello world",});
Instead of passing the values directly you can also pass an object containing a label
and value
property:
export const greetingFlag = flag<string>({ key: 'greeting', options: [ { label: 'Hello world', value: 'Hello world' }, { label: 'Hi', value: 'Hi' }, { label: 'Hola', value: 'Hola' } ], decide: () => "Hello world",});
To pass objects you must specify a label
and value
property:
export const greetingFlag = flag<string>({ key: 'greeting', options: [{ label: 'Hello world', value: {/* your object here */} }],});
The Flags SDK uses the declared options for multiple purposes:
-
Efficiently encode the flag values into the URL
The
precompute
function generates a short code which your appliciation then transports through the URL. The URLs must remain fairly short for the system to stay efficient. When a feature flag'sdecide
function returns a value not explicitly declared inoptions
the whole value needs to be inlined into thecode
which can quickly exceed the URL size limits. Especially for ISR the URL length needs to stay below 1024 characters. -
Generate the possible permutations flags
The
generatePermutations
function generates all possible combinations of flags for prerendering at build time. The function needs to know the available options for each flag to generate the possible permutations. It can only generate and prerender options decalred by the flag. -
Show the available options in the Flags Explorer
All options declared for a flag are shown in the Flags Explorer. If present, the
label
is used as the option name.
Example
See the Marketing Pages example which implements this pattern, and further shows how to permanently target users using cookies.