> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://contentful.com/developers/docs/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://contentful.com/developers/docs/_mcp/server.

# Choose your setup

> Choose the right architecture, packages, and plugins for your Contentful Personalization implementation.

Before installing packages, decide on your architecture and which plugins you need. This guide helps you make those choices based on your framework, rendering requirements, and feature needs.

## Choose your architecture

Your architecture determines when personalization happens, in the browser, on the server, or at the edge.

| Architecture            | How it works                                                                                                      | Best for                                                                            |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| **Client-only**         | SDK runs in the browser after page load. Baseline content shows briefly before variant swaps in.                  | Simple setups where a brief flash of default content is acceptable                  |
| **Hybrid SSR + client** | Server or edge calls the API before rendering, then the client SDK takes over for ongoing interactions. No flash. | Most production setups — best user experience with full analytics                   |
| **Server-only**         | Server renders personalized HTML with no client SDK.                                                              | Static generation, email rendering, or environments where no client JavaScript runs |

> **Info**
>
> **NOTE**:
> We recommend using the **hybrid** approach when personalized HTML on first load matters to you. Use **client-only** when simplicity is your priority. Avoid **server-only** unless you have a specific reason as it limits analytics and experiment measurement significantly.

For hybrid and server-only implementation details, see [Edge and Server Side Rendering](/personalization/edge-and-server-side-rendering).

## Choose your packages

Start with the core packages for your framework, then add plugins based on your needs.

### Core packages

For most setups, install these three packages:

```bash
npm install @ninetailed/experience.js @ninetailed/experience.js-next @ninetailed/experience.js-utils-contentful
```

If you are not using Next.js (`@ninetailed/experience.js-next`), replace the framework package:

| Framework                | Framework package                 |
| ------------------------ | --------------------------------- |
| React (CRA, Vite, Remix) | `@ninetailed/experience.js-react` |
| Gatsby                   | `@ninetailed/experience.js-react` |
| Server-only (Node.js)    | `@ninetailed/experience.js-node`  |

> **Info**
>
> **NOTE**:
> We recommend using fixed package versions instead of a range selector. For example, `7.9.0` instead od `^7.9.0`. When adding or updating packages later on, make sure all packages are at the same exact version. Mixing versions causes unexpected behaviour.

### Plugin packages

Add plugins based on what you need:

| Plugin                 | Package                                              | When to add                                                                                                                             |
| ---------------------- | ---------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| **Insights**           | `@ninetailed/experience.js-plugin-insights`          | Almost always. Required if you want experiment results, component view tracking, or conversion measurement.                             |
| **SSR**                | `@ninetailed/experience.js-plugin-ssr`               | When using server-side or edge personalization with a client SDK. Provides profile continuity via cookies.                              |
| **Preview**            | `@ninetailed/experience.js-plugin-preview`           | Development and preview environments only. Provides a UI to test different audiences and experiences. Do not enable in production.      |
| **Privacy**            | `@ninetailed/experience.js-plugin-privacy`           | When you need GDPR consent management, or when privacy requirements must be met. Blocks events and features until the visitor consents. |
| **Google Tag Manager** | `@ninetailed/experience.js-plugin-google-tagmanager` | When you want personalization events forwarded to GTM.                                                                                  |
| **Segment**            | `@ninetailed/experience.js-plugin-segment`           | When you want personalization events forwarded to Segment.                                                                              |
| **Contentsquare**      | `@ninetailed/experience.js-plugin-contentsquare`     | When you use Contentsquare for digital experience analytics.                                                                            |

> **Info**
>
> **NOTE**:
> The Insights plugin (`@ninetailed/experience.js-plugin-insights`) is different from the base analytics plugin (`@ninetailed/experience.js-plugin-analytics`). For built-in experiment measurement and component insights, use the Insights plugin.

## Framework-specific guidance

### Next.js Pages Router

* Place `NinetailedProvider` in `pages/_app.tsx`.
* The Pages Router integration automatically tracks page changes.

> **Info**
>
> **NOTE**:
> Do not add manual `page()` calls for route navigation.

* Use `getStaticProps` with `revalidate` (ISR) for the best balance of performance and fresh content.
* For SSR/edge, add middleware and the SSR plugin.

#### Example

```
// in pages/_app.tsx

import { NinetailedProvider } from "@ninetailed/experience.js-next";
import { InsightsPlugin } from "@ninetailed/experience.js-plugin-insights";

export default function App({ Component, pageProps }) {
  return (
    <NinetailedProvider
      clientId="<your-client-id>"
      environment="<your-environment>"
      plugins={[
        new InsightsPlugin(),
        // Add other plugins as needed.
      ]}
    >
      <Component {...pageProps} />
    </NinetailedProvider>
  );
}
```

### Next.js App Router

* The provider must be in a Client Component. Create a `providers.tsx` wrapper with `'use client'` and place it in your root layout.
* The current SDKs do not auto-track App Router navigation. Add a dedicated page tracker component.
* Server Components cannot use personalization hooks. Fetch content in a Server Component and pass it to a Client Component that handles personalization.

#### Examples

```
// in app/providers.tsx

"use client";

import { NinetailedProvider } from "@ninetailed/experience.js-next";
import { InsightsPlugin } from "@ninetailed/experience.js-plugin-insights";

export function Providers({ children }) {
  return (
    <NinetailedProvider
      clientId="<your-client-id>"
      environment="<your-environment>"
      plugins={[
        new InsightsPlugin(),
        // Add other plugins as needed.
      ]}
    >
      {children}
    </NinetailedProvider>
  );
}
```

```
// in app/layout.tsx

import { Providers } from "./providers";

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

```
'use client';

import { useEffect } from 'react';
import { usePathname } from 'next/navigation';
import { useNinetailed } from '@ninetailed/experience.js-react';

export default function TrackPage() {
  const pathname = usePathname();
  const { page } = useNinetailed();

  useEffect(() => {
    void page();
    [page, pathname];
  });

  return null;
}
```

### Gatsby

* Place the provider in `gatsby-browser.js` (and `gatsby-ssr.js`). The provider needs to be declared both in the browser and the SSR file to avoid React hydration issues, such as, the server-generated React tree not matching the client-generated one.
* Personalization is client-side only — static HTML shows the baseline, and the SDK swaps variants after hydration
* Use the `GATSBY_` prefix for environment variables (e.g., `GATSBY_NINETAILED_CLIENT_ID`)

#### Example

```

// in gatsby-browser.js

import { NinetailedProvider } from "@ninetailed/experience.js-react";
import { InsightsPlugin } from "@ninetailed/experience.js-plugin-insights";

export const wrapRootElement = ({ element }) => (
  <NinetailedProvider
    clientId="<your-client-id>"
    environment="<your-environment>"
    plugins={[
      new InsightsPlugin(),
      // Add other plugins as needed.
    ]}
  >
    {element}
  </NinetailedProvider>
);
```

```
// in gatsby-ssr.js

export const wrapRootElement = ({ element }) => {
  // Reuse or redeclare the same implementation
  // as in gatsby-browser.js.
};
```

### Plain React (Vite)

* Place the provider at your app root
* Personalization is client-side only
* No SSR capabilities. Consider Next.js if you need server-side personalization.

#### Example

```
import { NinetailedProvider } from "@ninetailed/experience.js-react";
import { InsightsPlugin } from "@ninetailed/experience.js-plugin-insights";

export function App() {
  return (
    <NinetailedProvider
      clientId="<your-client-id>"
      environment="<your-environment>"
      plugins={[
        new InsightsPlugin(),
        // Add other plugins as needed.
      ]}
    >
      <YourApplication />
    </NinetailedProvider>
  );
}
```

## Environment variables

At minimum, for Next.js, you need:

| Variable                             | Purpose                                  |
| ------------------------------------ | ---------------------------------------- |
| `NEXT_PUBLIC_NINETAILED_CLIENT_ID`   | Your Contentful Personalization API key. |
| `NEXT_PUBLIC_NINETAILED_ENVIRONMENT` | The environment slug (usually `main`).   |

For edge or server-side setups, you may also need `NINETAILED_API_KEY` and `NINETAILED_ENVIRONMENT` without the `NEXT_PUBLIC_` prefix.

Add real values to `.env.local` and placeholders to `.env.example`. Never commit API keys or tokens to source control.

## Next steps

* [Quick start guide](/personalization/quick-start-guide)
* [Architecture and how it works](/personalization/architecture)
* [Experience SDK](/personalization/experience-sdk)