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

# Next.js

> Evaluate flags in Server Components and route handlers with a server key, and in client components with React hooks.

## Overview

In this guide you use shipd in a Next.js App Router app. Server Components and route handlers use `shipdit` with a server SDK key. Client components use `shipdit/client` and the React hooks with a client SDK key. There is no Next.js-specific package. You combine the [Node.js](/sdks/node) and [React](/sdks/react) setups.

## Which key goes where

Create one key of each type in **SDK keys** for the environment. See [Keys](/concepts/keys).

| Code | Entry point | Key | Environment variable |
| - | - | - | - |
| Server Components, route handlers, server actions | `shipdit` | `shipd_sdk_server_…` | `SHIPD_SDK_KEY` |
| Client components (`"use client"`) | `shipdit/client` | `shipd_sdk_client_…` | `NEXT_PUBLIC_SHIPD_SDK_KEY` |

```bash .env.local theme={null}
SHIPD_SDK_KEY=shipd_sdk_server_…
NEXT_PUBLIC_SHIPD_SDK_KEY=shipd_sdk_client_…
```

> [!WARNING]
>
> Next.js inlines every `NEXT_PUBLIC_` variable into the browser bundle. Never give the server key
> a `NEXT_PUBLIC_` name.

## Evaluate on the server

Create the server client in a module that only server code imports. Adding `import "server-only"` (from the [`server-only`](https://www.npmjs.com/package/server-only) package) is optional. It makes the build fail if a client component imports this file.

```ts src/lib/shipd.server.ts theme={null}
import "server-only";
import { createClient } from "shipdit";
import type { FlagDefinitions } from "@/generated/flags";

export const shipd = createClient<FlagDefinitions>({
  sdkKey: process.env.SHIPD_SDK_KEY!,
  defaults: {
    "new-checkout": false,
    "search-variant": "control",
    "max-retries": 3,
    "checkout-payload": { mode: "strict", showPromo: true },
  },
});
```

This client doesn't set `stream: true`, so it polls. To add realtime updates on a long-running Node.js server, see [Node.js](/sdks/node#create-the-client-module).

Await `ready()` before you evaluate, then pass per-request context:

```tsx src/app/checkout/page.tsx theme={null}
import { shipd } from "@/lib/shipd.server";
import { getSession } from "@/lib/auth";
import { CheckoutButton } from "./CheckoutButton";

export default async function CheckoutPage() {
  await shipd.ready();
  const session = await getSession();

  const newCheckout = shipd.isEnabled("new-checkout", {
    userId: session.user.id,
    attributes: { plan: session.user.plan },
  });

  return <CheckoutButton newCheckout={newCheckout} />;
}
```

`ready()` resolves once per client, so later requests don't wait. Route handlers work the same way:

```ts src/app/api/search/route.ts theme={null}
import { shipd } from "@/lib/shipd.server";

export async function GET(request: Request) {
  await shipd.ready();
  const userId = request.headers.get("x-user-id") ?? undefined;
  const engine = shipd.getVariant("search-variant", { userId });
  return Response.json({ engine });
}
```

Pass context on each server call. On the server client, `identify()` only records an identify event and never changes evaluation context.

## Evaluate in client components

Bind the hooks in a client module:

```ts src/lib/shipd-react.ts theme={null}
"use client";

import { createShipdReact } from "shipdit/client/react";
import type { FlagDefinitions } from "@/generated/flags";

export const { ShipdProvider, useFlag, useVariant, useReady, useShipdClient } =
  createShipdReact<FlagDefinitions>();
```

Create the browser client and provider in `providers.tsx`:

```tsx src/app/providers.tsx theme={null}
"use client";

import { createClient } from "shipdit/client";
import type { FlagDefinitions } from "@/generated/flags";
import { ShipdProvider } from "@/lib/shipd-react";

const client = createClient<FlagDefinitions>({
  sdkKey: process.env.NEXT_PUBLIC_SHIPD_SDK_KEY!,
  stream: true,
  defaults: {
    "new-checkout": false,
    "search-variant": "control",
  },
});

export function Providers({ children }: { children: React.ReactNode }) {
  return <ShipdProvider client={client}>{children}</ShipdProvider>;
}
```

```tsx src/app/layout.tsx theme={null}
import { Providers } from "./providers";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}
```

Identify the signed-in user from a client component inside `Providers`, as shown in [React](/sdks/react#identify-from-your-auth-layer).

## Avoid hydration mismatches

Client components are also rendered on the server. The server render and the first browser render can see different snapshots and identities, so a flag can evaluate differently in each. Use one of these patterns:

**Evaluate on the server and pass the value down.** The value is fixed in the HTML, so both renders agree. Use this for content that must be correct on first paint.

```tsx src/app/checkout/CheckoutButton.tsx theme={null}
"use client";

export function CheckoutButton({ newCheckout }: { newCheckout: boolean }) {
  return <a href={newCheckout ? "/checkout/new" : "/checkout"}>Checkout</a>;
}
```

**Gate on `useReady()`.** `useReady()` is `false` during the server render and the first browser render, so both output the same placeholder. The component renders the real value after the client is ready.

```tsx src/components/SearchBox.tsx theme={null}
"use client";

import { useReady, useVariant } from "@/lib/shipd-react";

export function SearchBox() {
  const ready = useReady();
  const engine = useVariant("search-variant");

  if (!ready) return <SearchSkeleton />;
  return engine === "semantic" ? <SemanticSearch /> : <ClassicSearch />;
}
```

## Next

* [React](/sdks/react)
* [Complete Next.js example](/examples/nextjs)
* [Identify users](/flags/identify-users)
* [SDK reference](/reference/sdk)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.