> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trevosdk.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Next.js

> Resolve variants on the server so the first paint is correct for everyone.

Everything in [React](/install/react) works in Next.js as-is. This page adds the one thing only
a server can do: resolve variants **before** the page renders, so first-time visitors never
see the control version flash.

`next` is an optional peer dependency (>= 14).

## 1. Middleware

Sets a stable `trevo_id` cookie so the server and browser bucket the same visitor
identically. One line plus a matcher:

```ts theme={null}
// middleware.ts
export { middleware } from '@trevosdk/browser/next';

export const config = {
  matcher: ['/((?!_next/static|_next/image|favicon.ico|.*\\..*).*)'],
};
```

Without this, the server has no identity to bucket on for a first-time visitor.

## 2. Resolve on the server

In a server component on the page running the experiment:

```tsx theme={null}
import { getTrevoBootstrap } from '@trevosdk/browser/next';
import { TrevoProvider } from '@trevosdk/browser/react';

export default async function Layout({ children }) {
  const bootstrap = await getTrevoBootstrap();   // { experimentKey: variantName }

  return (
    <TrevoProvider apiKey={process.env.NEXT_PUBLIC_TREVO_API_KEY} bootstrap={bootstrap}>
      {children}
    </TrevoProvider>
  );
}
```

`getTrevoBootstrap()` reads the `trevo_id` cookie and the API key from the environment, then
resolves every experiment with the same deterministic hash the browser uses. The client
starts with those assignments already in hand, so the first paint is correct.

It fails soft: if the config fetch fails, it returns an empty map and the client resolves on
its own as usual.

### Keeping most routes static

Reading a cookie forces a route to render dynamically. To confine that to the pages that
need it, skip the provider-wide bootstrap and pass a single experiment instead:

```tsx theme={null}
const bootstrap = await getTrevoBootstrap();

<CheckoutCta initialVariant={bootstrap['checkout-cta']} />
```

```tsx theme={null}
'use client';
const variant = useExperiment('checkout-cta', { initialVariant });
```

`initialVariant` takes precedence over the provider's `bootstrap`.

## 3. Track conversions as normal

```tsx theme={null}
'use client';
const trevo = useTrevo();
trevo?.track('purchase_completed', { value: 49.99 });
```

For conversions that happen on your backend — payment webhooks especially — use
[`@trevosdk/node`](/install/node) instead. Those events cannot be lost to an ad blocker or a
closed tab.

## Other backends

The Next.js entry is a thin wrapper. For any other server framework, the same primitives
live in `@trevosdk/browser/server`:

```ts theme={null}
import { resolveExperiments, TREVO_ID_COOKIE, generateBucketingId } from '@trevosdk/browser/server';

const variants = await resolveExperiments({
  apiKey,
  bucketingId: req.cookies[TREVO_ID_COOKIE] ?? generateBucketingId(),
});
```

`resolveExperiments()` never records an exposure — the client does that when the variant
actually renders.

## The rule that keeps client and server agreeing

Both sides must bucket on the **same identity at the same moment**: the user id when the
visitor is identified, otherwise the `trevo_id` cookie value. Anything else assigns one
person different variants on the server and the client, which corrupts the experiment.

The middleware plus `getTrevoBootstrap()` handle this for you. If you hand-roll with
`resolveExperiments()`, it is your responsibility.
