---
title: Next.js
description: Content analytics for Next.js App Router and Pages Router
icon: NextjsIcon
---

The Next.js adapter wraps your app in a `ContentProvider` and exposes a `useTrackView` hook that's SSR-safe. It detects build-time and skips emission automatically.

## Install

<Tabs items={['npm', 'bun', 'pnpm', 'yarn']}>
  <Tab value="npm">
    ```bash
    npm install @better-i18n/content
    ```
  </Tab>
  <Tab value="bun">
    ```bash
    bun add @better-i18n/content
    ```
  </Tab>
  <Tab value="pnpm">
    ```bash
    pnpm add @better-i18n/content
    ```
  </Tab>
  <Tab value="yarn">
    ```bash
    yarn add @better-i18n/content
    ```
  </Tab>
</Tabs>

## Configure

Add your Project ID and API key to `.env.local`. Find both in the dashboard under **Settings → General** and **Settings → API Keys**.

```bash title=".env.local"
NEXT_PUBLIC_BETTER_I18N_PROJECT_ID=your-org/your-project
NEXT_PUBLIC_BETTER_I18N_KEY=bi_pub_xxxxx
```

## Setup

Wrap your app with `ContentProvider` in a client component:

```tsx title="app/providers.tsx"
'use client'
import { ContentProvider } from '@better-i18n/content/adapters/nextjs'

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <ContentProvider
      config={{
        projectId: process.env.NEXT_PUBLIC_BETTER_I18N_PROJECT_ID!,
        apiKey: process.env.NEXT_PUBLIC_BETTER_I18N_KEY!,
      }}
    >
      {children}
    </ContentProvider>
  )
}
```

Mount it in your root layout:

```tsx title="app/layout.tsx"
import { Providers } from './providers'

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  )
}
```

## Track a view

```tsx title="app/blog/[slug]/page.tsx"
'use client'
import { useTrackView } from '@better-i18n/content/adapters/nextjs'

export default function BlogPost({ post }: { post: Post }) {
  useTrackView('content.view', {
    entryId: post.id,
    contentModel: 'blog',
    entrySlug: post.slug,
    language: post.locale,
  })

  return <article>{post.body}</article>
}
```

## SSR safety

`ContentProvider` creates the tracker synchronously, but `track()` is inherently SSR-safe — it checks `isBrowser()` internally and becomes a no-op on the server. `useTrackView` is also a no-op during SSR and during the Next.js build phase (`NEXT_PHASE=phase-production-build`).

If `useTrackView` or `useContent` is used outside of a `ContentProvider`, the SDK logs a one-time warning and disables tracking — it never throws or crashes your app.

## Next steps

- [Analytics API Reference](/sdk/analytics-api)
- [Analytics Overview](/sdk/analytics)
