
## Set Up the SDK

### AI-powered Setup

Setup Statsig in 90 seconds by copying this AI prompt into your IDE:

```text expandable Prompt
# Statsig SDK Integration for Next.js

You are a frontend engineer integrating the Statsig SDK into a **Next.js application**. Follow all steps below one by one:

---

## Full Integration Instructions

1. **Detect the package manager** by checking for:
   - `package-lock.json` → use `npm`
   - `yarn.lock` → use `yarn`
   - `pnpm-lock.yaml` → use `pnpm`

2. **Detect the Next.js router type** by checking for:
   - `app/` directory → **App Router**
   - `pages/` directory → **Pages Router**

3. **Install the Statsig package** using the correct package manager:

# For npm
npm install @statsig/react-bindings @statsig/session-replay @statsig/web-analytics

# For yarn
yarn add @statsig/react-bindings @statsig/session-replay @statsig/web-analytics

# For pnpm
pnpm add @statsig/react-bindings @statsig/session-replay @statsig/web-analytics

4. Add your Statsig client key to .env.local:

NEXT_PUBLIC_STATSIG_CLIENT_KEY=ask the user for their CLIENT KEY and use that input

5. Integrate Statsig into the app (auto-detect router type):

### If the project uses the App Router (has an app/ directory):

// Create app/my-statsig.tsx
"use client";

import React from "react";
import { LogLevel, StatsigProvider } from "@statsig/react-bindings";

export default function MyStatsig({ children }: { children: React.ReactNode }) {
  const id = typeof userID !== "undefined" ? userID : "a-user";

  const user = {
    userID: id,
    // Optional additional fields:
    // email: 'user@example.com',
    // customIDs: { internalID: 'internal-123' },
    // custom: { plan: 'premium' }
  };

  return (
    <StatsigProvider
      sdkKey={process.env.NEXT_PUBLIC_STATSIG_CLIENT_KEY!}
      user={user}
      options={{ logLevel: LogLevel.Debug }}
    >
      {children}
    </StatsigProvider>
  );
}

// Update app/layout.tsx to wrap children with MyStatsig
import MyStatsig from "./my-statsig";

export default function RootLayout({
  children,
}: Readonly<{ children: React.ReactNode }>) {
  return (
    <html lang="en">
      <body>
        <MyStatsig>
          {children} {/* Preserve all existing layout content */}
        </MyStatsig>
      </body>
    </html>
  );
}

### If the project uses the Pages Router (has a pages/ directory):

// Update pages/_app.tsx
import type { AppProps } from "next/app";
import { LogLevel, StatsigProvider } from "@statsig/react-bindings";

export default function App({ Component, pageProps }: AppProps) {
  const id = typeof userID !== "undefined" ? userID : "a-user";

  const user = {
    userID: id,
    // Optional additional fields:
    // email: 'user@example.com',
    // customIDs: { internalID: 'internal-123' },
    // custom: { plan: 'premium' }
  };

  return (
    <StatsigProvider
      sdkKey={process.env.NEXT_PUBLIC_STATSIG_CLIENT_KEY!}
      user={user}
      options={{ logLevel: LogLevel.Debug }}
    >
      <Component {...pageProps} /> {/* Preserve all existing pages */}
    </StatsigProvider>
  );
}

### Final Notes

- The system must **detect the router type** and **apply the correct integration automatically**.
- **Do not remove or change any existing JSX or layout structure**: only wrap the app with `StatsigProvider`.
- **Preserve the file's language**: if it's TypeScript (`.tsx`), keep it as TypeScript; if it's JavaScript (`.jsx`), keep it as JavaScript.
- After these steps, Statsig will be integrated across the entire app, with **feature gates, configs, and experiments** available everywhere.
```

### Manual Setup



Statsig supports both [Page Router](https://nextjs.org/docs/pages) & [App Router](https://nextjs.org/docs/app), with some differences in integration patterns.

{% tabs %}
{% tab title="App Router" %}
{% steps %}
{% step title="Set environment variables" %}
Add the keys to your .env.local file:

```bash .env.local
# the NEXT_PUBLIC_ prefix is required for this to be available on the client side
NEXT_PUBLIC_STATSIG_CLIENT_KEY=client-<REPLACE_WITH_YOUR_CLIENT_KEY>
STATSIG_SERVER_KEY=secret-<REPLACE_WITH_YOUR_SERVER_KEY>
```
{% /step %}

{% step title="Install packages" %}
For App Router, install the @statsig/next package:

{% codetabs %}
```bash NPM
npm i @statsig/next
```

```bash Yarn
yarn add @statsig/next
```

```bash PNPM
pnpm add @statsig/next
```
{% /codetabs %}
{% /step %}

{% step title="Add the StatsigBootstrapProvider" %}
The \<StatsigBootstrapProvider> creates both a Statsig Client and Server instance internally, and ["bootstraps"](/client/concepts/initialize#2-bootstrap-initialization) the client so it can render each page without a blocking network request. This keeps your app fast and is recommended for most users. If you need more control over your setup, refer to [Bootstrapping](#client-bootstrapping-recommended) and [React](/client/React) for more guidance.

Add this component around the content in your root `layout.tsx` file:

```tsx app/layout.tsx
import { StatsigBootstrapProvider } from "@statsig/next"

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {

  const user = {
    userID: "user-123", // add additional parameters as needed
  };
  return (
    <html lang="en">
      <body>
        <StatsigBootstrapProvider
          user={user}
          clientKey={process.env.NEXT_PUBLIC_STATSIG_CLIENT_KEY}
          serverKey={process.env.STATSIG_SERVER_KEY}
        >
          {children}
        </StatsigBootstrapProvider>
      </body>
    </html>
  );
}
```
{% /step %}
{% /steps %}
{% /tab %}

{% tab title="Page Router" %}
{% steps %}
{% step title="Set environment variables" %}
Add the keys to your .env.local file:

```bash .env.local
# the NEXT_PUBLIC_ prefix is required for this to be available on the client side
NEXT_PUBLIC_STATSIG_CLIENT_KEY=client-<REPLACE_WITH_YOUR_CLIENT_KEY>
```
{% /step %}

{% step title="Install packages" %}
Install the @statsig/react-bindings package:

{% codetabs %}
```bash NPM
npm i @statsig/react-bindings @statsig/web-analytics
```

```bash Yarn
yarn add @statsig/react-bindings @statsig/web-analytics
```

```bash PNPM
pnpm add @statsig/react-bindings @statsig/web-analytics
```
{% /codetabs %}
{% /step %}

{% step title="Add StatsigProvider to _app.tsx" %}
To integrate Statsig into your Page Router app you can add the `StatsigProvider` to your `_app.tsx` file.

There is a [full example](https://github.com/statsig-io/js-client-monorepo/tree/main/samples/next-js-pages-router-sample) in the samples directory of the javascript sdk.

```tsx pages/_app.tsx
import type { AppProps } from "next/app";

import {
  LogLevel,
  StatsigProvider,
} from "@statsig/react-bindings";
import { StatsigAutoCapturePlugin } from '@statsig/web-analytics';

export default function App({ Component, pageProps }: AppProps) {
  return (
    <StatsigProvider
      sdkKey={process.env.NEXT_PUBLIC_STATSIG_CLIENT_KEY!}
      user={{ userID: "a-user" }}
      options={{
        plugins: [ new StatsigAutoCapturePlugin() ],
      }}>
      <Component {...pageProps} />
    </StatsigProvider>
  );
}
```
{% /step %}
{% /steps %}
{% /tab %}
{% /tabs %}

Refer to the [User (StatsigUser)](/sdks/user) documentation for more information on the user property. After completing setup, you can check gates, experiments, and log events in any sub-file of layout.tsx.

## Use the SDK

### Checking a Feature Flag/Gate

Now that your SDK is initialized, let's check a [**Feature Gate**](/feature-flags/overview). Feature Gates can be used to create logic branches in code that can be rolled out to different users from the Statsig Console. Gates are always **CLOSED** or **OFF** (think `return false;`) by default.

{% tabs %}
{% tab title="App Router" %}
```tsx
'use client';

import { useGateValue } from "@statsig/react-bindings";

export default function Home() {
  const gate = useGateValue("my_gate");

  return (
    <div>
      Gate Value: {gate ? 'PASSED' : 'FAILED'}
    </div>
  );
}
```

{% callout type="note" %}
In an App Router app, you need to use the [`use client` directive](https://nextjs.org/docs/app/building-your-application/rendering/client-components) to ensure your logic runs on the frontend.
{% /callout %}
{% /tab %}

{% tab title="Page Router" %}
```tsx
import { useGateValue } from "@statsig/react-bindings";

export default function Home() {
  const gate = useGateValue("my_gate");

  return (
    <div>
      Gate Value: {gate ? 'PASSED' : 'FAILED'}
    </div>
  );
}
```
{% /tab %}
{% /tabs %}

### Reading a Dynamic Config

Feature Gates are useful for simple on/off switches with optional advanced user targeting. To send a different set of values (strings, numbers, etc.) to clients based on specific user attributes such as country, use **Dynamic Configs**. The API is similar to Feature Gates, but you get a complete JSON object you can configure on the server and fetch typed parameters from it. For example:

{% tabs %}
{% tab title="App Router" %}
```tsx
'use client';

import { useDynamicConfig } from "@statsig/react-bindings";

export default function Home() {
  const config = useDynamicConfig("my_dynamic_config");

  return (
    <div>
      Title: {config.get('title', 'Fallback Title')}
    </div>
  );
}
```

{% callout type="note" %}
In an App Router app, you need to use the [`use client` directive](https://nextjs.org/docs/app/building-your-application/rendering/client-components) to ensure your logic runs on the frontend.
{% /callout %}
{% /tab %}

{% tab title="Page Router" %}
```tsx
import { useDynamicConfig } from "@statsig/react-bindings";

export default function Home() {
  const config = useDynamicConfig("my_dynamic_config");

  return (
    <div>
      Title: {config.get('title', 'Fallback Title')}
    </div>
  );
}
```
{% /tab %}
{% /tabs %}

### Getting a Layer/Experiment

**Layers/Experiments** let you run A/B/n experiments. Two APIs are available, but Statsig recommends [layers](/experiments/layers-overview) for quicker iterations with parameter reuse.

{% tabs %}
{% tab title="App Router" %}
```tsx
'use client';

import { useExperiment, useLayer} from "@statsig/react-bindings";

export default function Home() {
  const layer = useLayer("my_experiment_layer");
  // or
  const experiment = useExperiment("my_experiment");

  return (
    <div>
      Title: {layer.get('title', 'Fallback Title')}
      {/* or */}
      Title: {experiment.get('title', 'Fallback Title')}
    </div>
  );
}
```

{% callout type="note" %}
In an App Router app, you need to use the [`use client` directive](https://nextjs.org/docs/app/building-your-application/rendering/client-components) to ensure your logic runs on the frontend.
{% /callout %}
{% /tab %}

{% tab title="Page Router" %}
```tsx
import { useExperiment, useLayer} from "@statsig/react-bindings";

export default function Home() {
  const layer = useLayer("my_experiment_layer");
  // or
  const experiment = useExperiment("my_experiment");

  return (
    <div>
      Title: {layer.get('title', 'Fallback Title')}
      {/* or */}
      Title: {experiment.get('title', 'Fallback Title')}
    </div>
  );
}
```
{% /tab %}
{% /tabs %}

## Parameter Stores

Parameter Stores hold a set of parameters for your app. These parameters can be remapped dynamically from a static value to a Statsig entity (Feature Gates, Experiments, and Layers), so you can decouple your code from the configuration in Statsig. Refer to [Parameter Stores](/client/concepts/parameter-stores) for details.

{% tabs %}
{% tab title="App Router" %}
```tsx
'use client';

import { useParameterStore} from "@statsig/react-bindings";

export default function Home() {
  const store = useParameterStore("my_param_store");

  return (
    <div>
      Title: {store.get('title', 'Fallback Title')}
    </div>
  );
}
```

{% callout type="note" %}
In an App Router app, you need to use the [`use client` directive](https://nextjs.org/docs/app/building-your-application/rendering/client-components) to ensure your logic runs on the frontend.
{% /callout %}
{% /tab %}

{% tab title="Page Router" %}
```tsx
import { useParameterStore} from "@statsig/react-bindings";

export default function Home() {
  const store = useParameterStore("my_param_store");

  return (
    <div>
      Title: {store.get('title', 'Fallback Title')}
    </div>
  );
}
```
{% /tab %}
{% /tabs %}

### Logging an Event

After setting up a Feature Gate or Experiment, you may want to track custom events to see how new features or experiment groups affect those events. Call the Log Event API for the event. You can also provide a value and metadata object to be logged with the event:

{% tabs %}
{% tab title="App Router" %}
```tsx
'use client';

import { useStatsigClient } from "@statsig/react-bindings";

export default function Home() {
  const { client } = useStatsigClient();

  return (
    <div>
      <button onClick={() => client.logEvent("my_custom_event")}>
        Click Me
      </button>
    </div>
  );
}
```

{% callout type="note" %}
In an App Router app, you need to use the [`use client` directive](https://nextjs.org/docs/app/building-your-application/rendering/client-components) to ensure your logic runs on the frontend.
{% /callout %}
{% /tab %}

{% tab title="Page Router" %}
```tsx
import { useStatsigClient } from "@statsig/react-bindings";

export default function Home() {
  const { client } = useStatsigClient();

  return (
    <div>
      <button onClick={() => client.logEvent("my_custom_event")}>
        Click Me
      </button>
    </div>
  );
}
```
{% /tab %}
{% /tabs %}

### Flushing Logged Events

`flush()` sends queued events immediately. Use `shutdown()` when your app is exiting.

{% tabs %}
{% tab title="App Router" %}
```tsx
'use client';

import { useStatsigClient } from "@statsig/react-bindings";

export default function Home() {
  const { client } = useStatsigClient();

  return (
    <div>
      <button
        onClick={async () => {
          await client.flush();
        }}
      >
        Flush Events
      </button>
    </div>
  );
}
```

{% callout type="note" %}
In an App Router app, you need to use the [`use client` directive](https://nextjs.org/docs/app/building-your-application/rendering/client-components) to ensure your logic runs on the frontend.
{% /callout %}
{% /tab %}

{% tab title="Page Router" %}
```tsx
import { useStatsigClient } from "@statsig/react-bindings";

export default function Home() {
  const { client } = useStatsigClient();

  return (
    <div>
      <button
        onClick={async () => {
          await client.flush();
        }}
      >
        Flush Events
      </button>
    </div>
  );
}
```
{% /tab %}
{% /tabs %}

## Session Replay

{% tabs %}
{% tab title="App Router" %}
```tsx
'use client';

import { StatsigProvider } from '@statsig/react-bindings';
import { StatsigSessionReplayPlugin } from '@statsig/session-replay';

export default function App({ children }: { children: React.ReactNode }) {
  return (
    <StatsigProvider
      sdkKey={process.env.NEXT_PUBLIC_STATSIG_CLIENT_KEY!}
      user={{ userID: 'a-user' }}
      options={{ plugins: [new StatsigSessionReplayPlugin()] }}
    >
      {children}
    </StatsigProvider>
  );
}
```
{% /tab %}

{% tab title="Page Router" %}
```tsx
import { StatsigProvider } from '@statsig/react-bindings';
import { StatsigSessionReplayPlugin } from '@statsig/session-replay';

export default function App({ Component, pageProps }) {
  return (
    <StatsigProvider
      sdkKey={process.env.NEXT_PUBLIC_STATSIG_CLIENT_KEY!}
      user={{ userID: 'a-user' }}
      options={{ plugins: [new StatsigSessionReplayPlugin()] }}
    >
      <Component {...pageProps} />
    </StatsigProvider>
  );
}
```
{% /tab %}
{% /tabs %}

## Web Analytics / Auto Capture

By including the [`@statsig/web-analytics`](https://www.npmjs.com/package/@statsig/web-analytics) package in your project, you can automatically capture common web events like clicks and page views.

For more information on filtering events, enabling console log capture, and other configuration options available in web analytics, refer to the [Web Analytics Configuration](/webanalytics/overview#event-filtering-and-console-configuration) documentation.

{% tabs %}
{% tab title="App Router" %}
```tsx
'use client';

import { StatsigProvider } from '@statsig/react-bindings';
import { StatsigAutoCapturePlugin } from '@statsig/web-analytics';

export default function App({ children }: { children: React.ReactNode }) {
  return (
    <StatsigProvider
      sdkKey={process.env.NEXT_PUBLIC_STATSIG_CLIENT_KEY!}
      user={{ userID: 'a-user' }}
      options={{ plugins: [new StatsigAutoCapturePlugin()] }}
    >
      {children}
    </StatsigProvider>
  );
}
```
{% /tab %}

{% tab title="Page Router" %}
```tsx
import { StatsigProvider } from '@statsig/react-bindings';
import { StatsigAutoCapturePlugin } from '@statsig/web-analytics';

export default function App({ Component, pageProps }) {
  return (
    <StatsigProvider
      sdkKey={process.env.NEXT_PUBLIC_STATSIG_CLIENT_KEY!}
      user={{ userID: 'a-user' }}
      options={{ plugins: [new StatsigAutoCapturePlugin()] }}
    >
      <Component {...pageProps} />
    </StatsigProvider>
  );
}
```
{% /tab %}
{% /tabs %}

## Stable ID

Stable ID provides a consistent device identifier. It lets you run [logged-out experiments](/guides/first-device-level-experiment) and target gates at the device level.

### How Stable ID Works

* On first initialization, the SDK generates a Stable ID and stores it in `localStorage` under `statsig.stable_id.<SDK_KEY_HASH>`.
* Subsequent sessions reuse the stored value. Each client SDK key has its own Stable ID entry.
* Local storage is scoped per domain, so cross-domain usage requires sharing the value manually (see below).

### Reading the Stable ID

{% tabs %}
{% tab title="JavaScript" %}
```tsx
const context = client.getContext();
console.log('Statsig StableID:', context.stableID);
```
{% /tab %}

{% tab title="React" %}
```tsx
import { useStatsigClient } from '@statsig/react-bindings';

function MyComponent() {
  const { client } = useStatsigClient();
  const context = client.getContext();

  return <div>{context.stableID}</div>;
}
```
{% /tab %}
{% /tabs %}

### Overriding the Stable ID

Provide a custom Stable ID through `StatsigUser.customIDs.stableID` if you already manage a durable device identifier.

{% tabs %}
{% tab title="JavaScript" %}
```tsx
import { StatsigClient, StatsigUser } from '@statsig/js-client';

const userWithStableID: StatsigUser = {
  customIDs: {
    stableID: 'my-custom-stable-id',
  },
};

const client = new StatsigClient('client-xyz', userWithStableID);
await client.updateUserAsync(userWithStableID);
```
{% /tab %}

{% tab title="React" %}
```tsx
import { StatsigProvider, useStatsigClient } from '@statsig/react-bindings';

function App() {
  return (
    <StatsigProvider
      sdkKey="client-xyz"
      user={{
        customIDs: { stableID: 'my-custom-stable-id' },
      }}
    >
      <div>Your App</div>
    </StatsigProvider>
  );
}

function MyComponent() {
  const { client } = useStatsigClient();
  useEffect(() => {
    client.updateUserAsync({
      customIDs: { stableID: 'my-custom-stable-id' },
    });
  }, [client]);
}
```
{% /tab %}
{% /tabs %}

{% callout type="note" %}
When you override the Stable ID, it is persisted to local storage, so subsequent sessions reuse your custom value.
{% /callout %}

### Sharing Stable ID Across Subdomains

Add this helper script before initializing the SDK and then copy the stored value onto your user object.

```html
<!-- cross domain id script -->
<script>!function(){let t="STATSIG_LOCAL_STORAGE_STABLE_ID";function e(){if(crypto&&crypto.randomUUID)return crypto.randomUUID();let t=()=>Math.floor(65536*Math.random()).toString(16).padStart(4,"0");return`$\{t()\}${t()}-$\{t()\}-4${t().substring(1)}-$\{t()\}-${t()}$\{t()\}${t()}`}let i=null,n=localStorage.getItem(t)||null;if(document.cookie.match(/statsiguuid=([\w-]+);?/)&&([,i]=document.cookie.match(/statsiguuid=([\w-]+);?/)),i&&n&&i===n);else if(i&&n&&i!==n)localStorage.setItem(t,i);else if(i&&!n)localStorage.setItem(t,i);else{let o=e();localStorage.setItem(t,o),function t(i){let n=new Date;n.setMonth(n.getMonth()+12);let o=window.location.host.split(".");o.length>2&&o.shift();let s=`.$\{o.join(".")\}`;document.cookie=`statsiguuid=${i||e()};Expires=$\{n\};Domain=${s};Path=/`}(o)}}();</script>

<!-- Manually attach stableID to user object -->
<script>
const userObj = {};
if (localStorage.getItem('STATSIG_LOCAL_STORAGE_STABLE_ID')) {
  userObj.customIDs = {
    stableID: localStorage.getItem('STATSIG_LOCAL_STORAGE_STABLE_ID'),
  };
}
const client = new Statsig.StatsigClient('<client-sdk-key>', userObj);
</script>
```

_Use this script at your discretion and test thoroughly._

### Aligning Stable ID Between Client and Server

To share Stable ID with a backend Statsig SDK, send the value with requests and persist it server-side when missing. The server can bootstrap the client with the same Stable ID.

```tsx
// Server: ensure Stable ID exists, then return initialize response for the client
const values = Statsig.getClientInitializeResponse(user, YOUR_CLIENT_KEY, {
  hash: 'djb2',
});

// Client: apply the server-provided values and initialize synchronously
const { values, user: verifiedUser } = await fetch('/init-statsig-client', {
  method: 'POST',
  body: loadUserData(),
}).then((res) => res.json());

const myClient = new StatsigClient(YOUR_CLIENT_KEY, verifiedUser);
myClient.dataAdapter.setData(values);
myClient.initializeSync();
```

## Lifecycle & Advanced Usage

## Shutting Statsig Down

The SDK keeps event logs in the client cache and flushes them periodically to save data and battery usage. Because of this, the SDK may not have flushed some events when your app shuts down.

To ensure all logged events are flushed or saved locally, shut down Statsig when your app is closing:

{% tabs %}
{% tab title="App Router" %}
```tsx
'use client';

import { useEffect } from "react";
import { useStatsigClient } from "@statsig/react-bindings";

export default function Home() {
  const { client } = useStatsigClient();

  useEffect(() => {
    return () => {
      void client.shutdown();
    };
  }, [client]);

  return null;
}
```

{% callout type="note" %}
In an App Router app, you need to use the [`use client` directive](https://nextjs.org/docs/app/building-your-application/rendering/client-components) to ensure your logic runs on the frontend.
{% /callout %}
{% /tab %}

{% tab title="Page Router" %}
```tsx
import { useEffect } from "react";
import { useStatsigClient } from "@statsig/react-bindings";

export default function Home() {
  const { client } = useStatsigClient();

  useEffect(() => {
    return () => {
      void client.shutdown();
    };
  }, [client]);

  return null;
}
```
{% /tab %}
{% /tabs %}

## Advanced Setup

### Client Bootstrapping (Recommended)

{% tabs %}
{% tab title="App Router" %}
```ts app/api/statsig-bootstrap/route.ts
import { Statsig, StatsigUser } from '@statsig/statsig-node-core';

export async function POST(request: Request): Promise<Response> {
  const body = await request.json();
  const user = new StatsigUser(body?.user ?? {});

  // Ensure server SDK is initialized at startup
  // await Statsig.initialize(process.env.STATSIG_SERVER_KEY!);

  const values = Statsig.getClientInitializeResponse(user, {
    hashAlgorithm: 'djb2',
  });
  return new Response(JSON.stringify(values), { status: 200 });
}
```

```tsx app/layout.tsx
import { StatsigBootstrapProvider } from '@statsig/next';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  const user = { userID: 'user-123' };
  return (
    <html lang="en">
      <body>
        <StatsigBootstrapProvider
          user={user}
          clientKey={process.env.NEXT_PUBLIC_STATSIG_CLIENT_KEY}
          serverKey={process.env.STATSIG_SERVER_KEY}
        >
          {children}
        </StatsigBootstrapProvider>
      </body>
    </html>
  );
}
```
{% /tab %}

{% tab title="Page Router" %}
```ts pages/api/statsig-bootstrap.ts
import type { NextApiRequest, NextApiResponse } from 'next';
import { Statsig, StatsigUser } from 'statsig-node'; // legacy Node SDK for pages router

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse<string>,
) {
  if (req.method !== 'POST') {
    res.status(400).send('/statsig-bootstrap only supports POST');
    return;
  }

  // Ensure server SDK is initialized at startup
  // await Statsig.initialize(process.env.STATSIG_SERVER_KEY!);

  const { user } = JSON.parse(req.body) as { user: StatsigUser };
  const values = Statsig.getClientInitializeResponse(user, { hash: 'djb2' });
  res.status(200).send(JSON.stringify(values));
}
```

```tsx pages/_app.tsx
import type { AppProps } from 'next/app';
import { StatsigProvider } from '@statsig/react-bindings';
import { StatsigClient } from '@statsig/js-client';
import { useEffect, useMemo, useState } from 'react';

export default function App({ Component, pageProps }: AppProps) {
  const user = useMemo(() => ({ userID: 'a-user' }), []);
  const [client, setClient] = useState<StatsigClient | null>(null);

  useEffect(() => {
    (async () => {
      const res = await fetch('/api/statsig-bootstrap', {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({ user }),
      });
      const initializeValues = await res.json();
      const inst = new StatsigClient(
        process.env.NEXT_PUBLIC_STATSIG_CLIENT_KEY!,
        user,
        { initializeValues },
      );
      await inst.initializeAsync();
      setClient(inst);
    })();
  }, [user]);

  if (!client) {
    return null;
  }

  return (
    <StatsigProvider client={client}>
      <Component {...pageProps} />
    </StatsigProvider>
  );
}
```
{% /tab %}
{% /tabs %}

### Proxying Network Traffic (Optional)

{% tabs %}
{% tab title="App Router" %}
```ts app/proxy/initialize/route.ts
// Note: Using generic path names like "proxy" instead of "statsig-proxy"
// to prevent ad blockers from blocking these requests
import { generateBootstrapValues } from './statsig-backend';

export async function POST(request: Request): Promise<Response> {
  const json = await request.json();
  if (!json || typeof json !== 'object') {
    return new Response(null, { status: 400 });
  }
  const data = await generateBootstrapValues();
  return new Response(data);
}
```

```ts app/proxy/search.ts
// Note: Using generic path names like "search" instead of "log_event" or "events"
// to prevent ad blockers from blocking these requests
type ExtendedRequestInit = RequestInit & { duplex?: 'half' | 'full' };

export async function POST(request: Request): Promise<Response> {
  const tail = request.url.split('?').pop();
  const logEventUrl = `https://events.statsigapi.net/v1/log_event?${tail}`;
  const fetchOptions: ExtendedRequestInit = {
    method: 'POST',
    body: request.body,
    headers: request.headers,
    duplex: 'half',
  };
  return fetch(logEventUrl, fetchOptions);
}
```
{% /tab %}

{% tab title="Page Router" %}
```ts pages/api/proxy/initialize.ts
// Note: Using generic path names like "proxy" instead of "statsig-proxy"
// to prevent ad blockers from blocking these requests
import type { NextApiRequest, NextApiResponse } from 'next';
import { StatsigUser } from 'statsig-node';
import { getStatsigValues } from '@/pages/statsig-backend';

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse<string>,
) {
  if (req.method !== 'POST') {
    res.status(400).send('/initialize only supports POST');
    return;
  }
  const { user } = JSON.parse(req.body) as { user: StatsigUser };
  const values = await getStatsigValues(user);
  res.status(200).send(values);
}
```

```ts pages/api/proxy/search.ts
// Note: Using generic path names like "search" instead of "log_event" or "events"
// to prevent ad blockers from blocking these requests
import type { NextApiRequest, NextApiResponse } from 'next';

type ExtendedRequestInit = RequestInit & { duplex?: 'half' | 'full' };

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse<string>,
): Promise<void> {
  if (req.method !== 'POST') {
    res.status(400).send('/search only supports POST');
    return;
  }

  let logEventUrl = `https://events.statsigapi.net/v1/log_event`;
  const queryParams = [] as string[];
  for (const [key, value] of Object.entries(req.query)) {
    queryParams.push(`$\{key\}=${value}`);
  }
  if (queryParams.length > 0) {
    logEventUrl += '?' + queryParams.join('&');
  }

  const fetchOptions: ExtendedRequestInit = {
    method: 'POST',
    body: req.body as BodyInit,
    headers: req.headers as HeadersInit,
    duplex: 'half',
  };

  try {
    const response = await fetch(logEventUrl, fetchOptions);
    if (!response.ok) {
      res.status(500).send('Failed to log event');
      return;
    }
    const body = await response.text();
    res.status(response.status).send(body);
  } catch (err) {
    res.status(500).send('Failed to log event: ' + err);
  }
}
```
{% /tab %}
{% /tabs %}

```ts
// Assign URLs when creating the client
const inst = new StatsigClient(clientSdkKey, user, {
  networkConfig: {
    logEventUrl: '/api/proxy/search',
    initializeUrl: '/api/proxy/initialize',
    logEventCompressionMode: 'Forced',
  },
  disableCompression: true,
  disableStatsigEncoding: true,
});
```

## Static Site Generation (SSG)

Static Site Generation renders HTML at build time. Because static HTML can't respond to per-user values, experimenting on SSG content requires one of these patterns:

* Use Edge Middleware with Statsig's Edge Config Adapter for zero-latency redirects.
* Isolate Statsig usage to hydrated client components only.

```tsx
// Create a single client and share it across multiple StatsigProviders
const myStatsigClient = new StatsigClient(YOUR_SDK_KEY, user, options);
await myStatsigClient.initializeAsync();

<StatsigProvider client={myStatsigClient}>
  <YourComponent />
</StatsigProvider>

<StatsigProvider client={myStatsigClient}>
  <AnotherComponent />
</StatsigProvider>
```

## Statsig Options

{% parameter name="loggingEnabled" type="LoggingEnabledOption" %}
Controls logging behavior.

* `browser-only` (default): log events from browser environments.
* `disabled`: never send events.
* `always`: log in every environment, including non-browser contexts.
{% /parameter %}

{% parameter name="disableLogging" type="boolean" %}
Use `loggingEnabled: 'disabled'` instead.
{% /parameter %}

{% parameter name="disableStableID" type="boolean" %}
Skip generating a device-level Stable ID.
{% /parameter %}

{% parameter name="disableEvaluationMemoization" type="boolean" %}
Recompute every evaluation instead of using the memoized result.
{% /parameter %}

{% parameter name="initialSessionID" type="string" %}
Override the generated session ID.
{% /parameter %}

{% parameter name="enableCookies" type="boolean" %}
Persist Stable ID in cookies for cross-domain tracking.
{% /parameter %}

{% parameter name="disableStorage" type="boolean" %}
Prevent any local storage writes (disables caching).
{% /parameter %}

{% parameter name="networkConfig" type="NetworkConfig" %}
Override network endpoints per request type.
{% /parameter %}

{% accordion title="Network Config Options" %}
{% parameter name="api" type="string" %}
Base URL for all requests. The SDK appends endpoint paths like `/initialize` and `/rgstr`; append `/v1` when your proxy expects it.
{% /parameter %}

{% parameter name="initializeUrl" type="string" %}
Endpoint for initialization requests only. Takes precedence over `api` for `/initialize`.
{% /parameter %}

{% parameter name="initializeFallbackUrls" type="string[]" %}
Fallback endpoints for initialization requests only. This doesn't create a generic fallback for `api`.
{% /parameter %}

{% parameter name="logEventUrl" type="string" %}
Endpoint for event uploads.
{% /parameter %}

{% parameter name="logEventFallbackUrls" type="string[]" %}
Fallback endpoints for event uploads only. This doesn't create a generic fallback for `api`.
{% /parameter %}

{% parameter name="networkTimeoutMs" type="number" %}
Request timeout in milliseconds.
{% /parameter %}

{% parameter name="preventAllNetworkTraffic" type="boolean" %}
Disable all outbound requests; combine with `loggingEnabled: 'disabled'` to silence log warnings.
{% /parameter %}

{% parameter name="networkOverrideFunc" type="function" %}
Provide custom transport (e.g., Axios).
{% /parameter %}
{% /accordion %}

{% parameter name="environment" type="StatsigEnvironment" %}
Set environment-wide defaults (for example `{ tier: 'staging' }`).
{% /parameter %}

{% parameter name="logLevel" type="LogLevel" %}
Console verbosity.
{% /parameter %}

{% parameter name="loggingBufferMaxSize" type="number" %}
Max events per log batch.
{% /parameter %}

{% parameter name="loggingIntervalMs" type="number" %}
Interval between automatic flushes.
{% /parameter %}

{% parameter name="overrideAdapter" type="OverrideAdapter" %}
Modify evaluations before returning them.
{% /parameter %}

{% parameter name="includeCurrentPageUrlWithEvents" type="boolean" %}
Attach the current page URL to logged events.
{% /parameter %}

{% parameter name="disableStatsigEncoding" type="boolean" %}
Send requests without Statsig-specific encoding.
{% /parameter %}

{% parameter name="logEventCompressionMode" type="LogEventCompressionMode" %}
Control compression for batched events.
{% /parameter %}

{% parameter name="disableCompression" type="boolean" %}
Use `logEventCompressionMode` instead.
{% /parameter %}

{% parameter name="dataAdapter" type="EvaluationsDataAdapter" %}
Provide a custom data adapter to control caching/fetching.
{% /parameter %}

{% parameter name="customUserCacheKeyFunc" type="CustomCacheKeyGenerator" %}
Override cache key generation for stored evaluations.
{% /parameter %}

## Additional Resources

* [JavaScript Client SDK](/client/javascript-sdk)
* [React Client SDK](/client/React)
* [Initialization Concepts](/client/concepts/initialize)
