# Use Hypertune on static pages

## 1. Set up Hypertune

To use feature flags and run experiments on static pages without layout shift or flickering, first integrate Hypertune using one of the quickstarts:

* [Next.js (App Router) quickstart](https://docs.hypertune.com/getting-started/next.js-app-router-quickstart) with the **(Static) Client Components** setup
* [Next.js (Pages Router) quickstart](https://docs.hypertune.com/getting-started/next.js-pages-router-quickstart) with the **Client-side Rendering** setup

These quickstarts instruct you to wrap your page with the generated `<HypertuneHydrator>` and `<HypertuneRootProvider>` components, passing `dehydratedState` and `rootArgs` from the server. However, while this avoids layout shift, it requires `getHypertune`, which makes the page dynamic.

Instead, this guide shows you how to:

* Generate a static variant of your page for each combination of flag values.
* Use Middleware to route each visitor to the correct variant.

## 2. Define the flags for your page

Create an array with the flags that you want to use on your page:

{% code title="lib/constants.ts" %}

```typescript
import { FlagPath } from '@/generated/hypertune'

export const offerPageFlagPaths: FlagPath[] = [
  'exampleFlag',
  'enableDesignV2',
]
```

{% endcode %}

## 3. Add a Dynamic Segment

Add a Dynamic Segment to your page called `[encodedFlagValues]`. This will hold the encoded flag values in the URL.

Then, on your page:

* Use the generated `decodeFlagValues` function to access flag values.
* Add the generated `<HypertuneClientLogger>` component so flag evaluations and experiment exposures are logged on the client.
* Return an empty array from `generateStaticParams` to enable ISR. This ensures that once a static variant of your page is generated, it is cached and reused.

{% code title="app/offer/\[encodedFlagValues]/page.tsx" %}

```typescript
import { decodeFlagValues } from '@/generated/hypertune'
import { HypertuneClientLogger } from '@/generated/hypertune.react'
import { offerPageFlagPaths } from '@/lib/constants'

export async function generateStaticParams() {
  // Enable ISR
  return []
}

export default async function Page({
  params,
}: {
  params: { encodedFlagValues: string }
}) {
  const { exampleFlag, enableDesignV2 } = decodeFlagValues(
    params.encodedFlagValues,
    offerPageFlagPaths
  )

  return (
    <div>
      <div>Example Flag: {String(exampleFlag)}</div>
      <div>Enable Design V2: {String(enableDesignV2)}</div>
      <HypertuneClientLogger flagPaths={offerPageFlagPaths} />
    </div>
  )
}
```

{% endcode %}

## 4. Encode flags in Middleware

In Middleware, match on your static page, encode the flag values for the visitor, and rewrite the URL to include them:

{% code title="middleware.ts" %}

```typescript
import { NextRequest, NextResponse } from 'next/server'
import { flagFallbacks } from '@/generated/hypertune'
import { offerPageFlagPaths } from '@/lib/constants'
import getHypertune from '@/lib/getHypertune'

export const config = {
  matcher: '/offer',
}

export async function middleware(request: NextRequest) {
  const hypertune = await getHypertune()

  const encodedFlagValues = hypertune.encodeFlagValues({
    flagFallbacks,
    flagPaths: offerPageFlagPaths,
  })

  // Rewrites the request to include the encoded flag values
  const nextUrl = new URL(
    `/offer/${encodedFlagValues}${request.nextUrl.search}`,
    request.url
  )

  return NextResponse.rewrite(nextUrl, { request })
}
```

{% endcode %}

## Summary

1. A visitor requests `/offer`.
2. Middleware computes their flag values, encodes them, and rewrites the request to `/offer/[encodedFlagValues]`.
3. Your page decodes the values and renders the correct static variant.
4. Once generated, that variant is cached via ISR for future visitors who have the same flag values.

## Benefits

* Fully static pages with feature flags and experiments.
* No hydration mismatch, layout shift, or flickering.
* Variants cached at the edge for fast subsequent loads.
