# Next.js & React SPA integration

Install the FlowConsent banner in a Next.js app (App Router or Pages Router) and enable SPA Mode so consent is re-checked on every client-side navigation.

> Canonical: https://www.flowconsent.com/en/doc/nextjs-integration
> Last updated: 2026-05-31
The FlowConsent banner is a single self-contained `<script>` — the same snippet works on Next.js, Nuxt, SvelteKit, and any custom-code app, exactly like it does on Webflow, Framer, or WordPress. There is **one extra step** for single-page apps: enable **SPA Mode** so consent is re-checked when the user navigates client-side.

## Why a SPA needs one extra step

On a classic multi-page site, every navigation is a full page reload, so the banner re-runs and re-blocks tracking scripts on each page. A Single Page Application (Next.js, React, Vue…) navigates with `history.pushState` — **no page reload** — so without SPA Mode, scripts injected after the first page would not be re-blocked on subsequent routes.

SPA Mode hooks `pushState`, `replaceState`, and `popstate` to re-verify consent on every client-side navigation.

## Step 1 — Add the script

The banner already ships Google Consent Mode v2 defaults inside the bundle, so the snippet stays minimal: just load the script as early as possible.

### App Router (`app/layout.tsx`)

```tsx
import Script from 'next/script'

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        {children}
        {/* FlowConsent CMP */}
        <Script
          src="https://YOUR-LICENSE-CODE.consent.flowconsent.com/cookie-manager.js?code=YOUR_LICENSE_CODE"
          strategy="beforeInteractive"
        />
      </body>
    </html>
  )
}
```

`strategy="beforeInteractive"` makes Next.js load the script before hydration, so prior blocking is in place before any tracking fires.

### Pages Router (`pages/_document.tsx`)

```tsx
import { Html, Head, Main, NextScript } from 'next/document'

export default function Document() {
  return (
    <Html>
      <Head>
        {/* FlowConsent CMP — keep it first in <Head> */}
        <script src="https://YOUR-LICENSE-CODE.consent.flowconsent.com/cookie-manager.js?code=YOUR_LICENSE_CODE" />
      </Head>
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  )
}
```

You'll find your exact snippet (with your deployment URL and license code) in **Builder → Deployment → Integration**, where a **Next.js** tab generates this code for you.

## Step 2 — Enable SPA Mode

In the Builder, open the **Settings** tab and turn on **SPA Mode**. Redeploy your banner so the change ships.

With SPA Mode on, FlowConsent will:

- Intercept `history.pushState` and `history.replaceState`
- Listen for `popstate` events
- Re-block uncovered tracking scripts on each navigation
- Emit a `flowconsent:navigation` event you can listen to

## Step 3 — Let FlowConsent manage tracking scripts

Do **not** load tracking scripts (Google Analytics, Meta Pixel…) with `next/script`. Instead, declare them in **Builder → Services** and let FlowConsent inject them once consent is given.

```tsx
// Avoid — tracking script outside FlowConsent's control
<Script src="https://www.googletagmanager.com/gtag/js?id=G-XXXXX" strategy="afterInteractive" />

// Prefer — configure Google Analytics in Builder → Services
```

## Optional — React to consent in your app

FlowConsent dispatches events you can subscribe to:

```tsx
import { useEffect } from 'react'

export function useConsentListener() {
  useEffect(() => {
    const onConsent = (event) => {
      const { preferences } = event.detail
      if (preferences?.analytics) {
        // analytics consent granted — initialise your own analytics
      }
    }
    window.addEventListener('flowconsent:consent', onConsent)
    return () => window.removeEventListener('flowconsent:consent', onConsent)
  }, [])
}
```

## Verification checklist

- [ ] Script is in `app/layout.tsx` (App Router) or `pages/_document.tsx` (Pages Router)
- [ ] App Router uses `strategy="beforeInteractive"`
- [ ] **SPA Mode** is enabled in the Builder and the banner is redeployed
- [ ] No `next/script` for tracking scripts — they're configured in Services
- [ ] Banner appears on first load
- [ ] No tracking requests in the Network tab before clicking **Accept**
- [ ] Consent persists across client-side navigations
