Precompute
Using the precompute pattern in Next.js
This page shows how to implement the precompute pattern in Next.js to keep pages static, even when multiple feature flags are used on a single page, or even when feature flags are used across multiple pages. Ensure you've read about the general precompute concept to understand the pattern and benefits:
Manual approach
It's possible to manually create variants of page by creating two versions of the same page. For example, app/home-a/page.tsx
and app/home-b/page.tsx
. Then, use Edge Middleware to rewrite the request either to /home-a
or /home-b
.
This approach works well for simple cases, but has a few downsides:
- It can be cumbersome having to maintain both
/home-a/page.tsx
and/home-b/page.tsx
. - It 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.
Precompute pattern
Use the precompute functionality of the Flags SDK to work around the limitations of the manual approach. Use the precompute pattern to keep pages static, even when multiple feature flags are used on a single page, or even when feature flags are used across multiple pages.
Prerequisites
Ensure you have a FLAGS_SECRET
environment variable set. This variable is used by precompute
to encrypt the precomputation result into the URL. The FLAGS_SECRET
environment variable is required for precompute to work. It must contain a random string of 32 characters, encoded as Base64.
Generate a random value using the following command:
Create the environment variable on Vercel, and fill with the generated random value:
Pull the environment variable to your local project:
Alternatively, set the FLAGS_SECRET
environment variable locally in .env.local
:
1. Create flags to be precomputed
Export one or multiple flags as an array to be precomputed.
2. Precompute flags in middleware
Import and pass the group of flags to the precompute
function in middleware. Then, forward the precomputation result (code
) to the underlying page using an URL rewrite:
3. Access 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:
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)
You can enable Incremental Static Regeneration (ISR) to cache generated pages after their initial render:
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.
Opting into build-time rendering (optional)
The flags/next
submodule exposes the generatePermutations
helper function for generating pages for different combinations of flags at build time. This function is called and takes a list of flags and returns an array of strings representing each combination of flags:
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.
Pages Router
If you're using the Pages Router, you need to pass a flag to generatePermutations
which accepts the code from context
and the group of flags.
You also need to specify a getStaticPaths
function which can return the permutations to generate at build time or an empty array to use ISR.
Declaring available options (optional)
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:
Instead of passing the values directly you can also pass an object containing a label
and value
property:
To pass objects you must specify a label
and value
property:
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.
Adjusting the setup
This section shows how to adapt the precompute pattern to different scenarios.
Precomputing a single page only
The examples above use a single top-level group of flags, which will opt all pages nested under app/[code]
into precomputation. Instead of opting the whole application into precomputation, you can also precompute a single page only.
For example, if you want to precompute the /pricing
page only:
- Move your pricing page from
app/pricing/page.tsx
toapp/pricing/[pricingCode]/page.tsx
- Export a
pricingFlags
array of flags from yourflags.ts
file, containing all flags used by the pricing page - Run Edge Middleware for requests to
/pricing
, and passpricingFlags
to theprecompute
function - Adjust the rewrite in Edge Middleware to rewrite requests from
/pricing
to/pricing/[pricingCode]
- Use the
pricingFlags
array to access the precomputed result inapp/pricing/[pricingCode]/page.tsx
when using Flags
Precomputing a subset of pages
This section describes an alternative folder structure where only a part of the page tree makes use of precomputation.
So far the examples have used a single top-level group of flags under app/[code]
.
Instead, you can nest precomputed flags under a folder like app/precomputed/[code]
. This makes it clear that only those pages will have access to the precomputed flags.
Adjust the rewrite in Edge Middleware to include the precomputed
segment.
Note that you will need to manually maintain the paths in Edge Middleware for which the rewrite should run.
Multiple groups
Define multiple groups of flags to avoid unnecessarily generating permutations for flags which are not used by all pages. This control allows you to only generate permutations of the precise flags used by eacha subset of your pages.
For example, you can have a root group of flags which apply to all pages, and a nested group of flags which only apply to a single page or subset of pages. Create a root [rootCode]
at the root of your application for the common flags, and a [pricingCode]
for the flags used by the pricing page.
The file tree would look like this:
To use this pattern, you need to adjust Edge Middleware:
- Precompute both groups of flags by calling
precompute
for each group - Adjust the rewrite to add the precomputed codes at the right segments for each group of flags
When calling the feature flag, ensure you specify the code and group of flags it was precomputed for. For example, use await discountFlag(pricingCode, pricingFlags)
to access the precomputed value of the discountFlag
that is part of the pricingFlags
group.
Combinatory explosion
Using the precompute pattern for pages which consist of many feature flags, or feature flags which have many possible values will lead to an exponential increase in the number of permutations that exist of given page.
- Build time will increase if you rely on build time rendering.
- Cache hit rates will decrease if you rely on lazily generating the permutations.
Be mindful of the flags you precompute, and whether you pregenerate permutations at build time or on demand.
You can manually specify which permutations you generate at build time by passing a second argument to generatePermutations
. See the API Reference for more information. All remaining permutations will be generated on demand the first time they are requested.