Next.js 15 Pages Router integration
This guide assumes a Next.js 15 project using the Pages Router (pages/directory). By the end you'll have server-side flag evaluation in getServerSideProps with the datafile hydrated into the client provider so flags render correctly on the first paint — no loading flash — using @avsbhq/next and @avsbhq/react.
Install
1npm install @avsbhq/next@^1 @avsbhq/react@^1 @avsbhq/browser@^1Obtain your SDK key
Open Settings > Environments in your A vs B project and copy the SDK key for the target environment. Add it to your environment file:
1AVSB_SDK_KEY=sdk_production_xxxxxxxxxxxxxxxxNEXT_PUBLIC_. The Pages Router only exposes NEXT_PUBLIC_ variables to the browser.Wrap _app with the provider
Create a client-side AvsbProvider in pages/_app.tsx. Pass the bootstrap prop that getServerSideProps will hydrate into page props. The provider stays mounted across client navigations.
1import type { AppProps } from 'next/app'2import { AvsbProvider } from '@avsbhq/react'3import type { FlagDatafile } from '@avsbhq/core'4
5interface AvsbPageProps {6 avsbBootstrap?: FlagDatafile7}8
9export default function App({ Component, pageProps }: AppProps<AvsbPageProps>) {10 return (11 <AvsbProvider12 sdkKey={process.env.NEXT_PUBLIC_AVSB_SDK_KEY!}13 bootstrap={pageProps.avsbBootstrap}14 >15 <Component {...pageProps} />16 </AvsbProvider>17 )18}sdkKey in the browser for polling updates. Expose a public key via NEXT_PUBLIC_AVSB_SDK_KEY (safe to expose — it is a read-only evaluation key, not a write key).Fetch bootstrap data in getServerSideProps
Use getDatafile from @avsbhq/next/server to pre-fetch the datafile on the server for every page that needs flags without a loading state. Pass the serialised datafile as avsbBootstrap in props.
1import type { GetServerSideProps } from 'next'2import { getDatafile } from '@avsbhq/next/server'3import type { FlagDatafile } from '@avsbhq/core'4
5interface Props {6 avsbBootstrap: FlagDatafile7}8
9export const getServerSideProps: GetServerSideProps<Props> = async (ctx) => {10 const datafile = await getDatafile(process.env.AVSB_SDK_KEY!)11 return { props: { avsbBootstrap: datafile } }12}13
14export default function DashboardPage() {15 // Flags are ready synchronously — no loading flash.16 return <Dashboard />17}Read a flag
1import { useFlag } from '@avsbhq/react'2
3export function HeroSection() {4 const heroVariant = useFlag('homepage-hero', 'control')5
6 if (heroVariant.value === 'variant-a') return <HeroVariantA />7 if (heroVariant.value === 'variant-b') return <HeroVariantB />8 return <HeroControl />9}Track an event
1import { useTrack } from '@avsbhq/react'2
3export function CheckoutButton() {4 const track = useTrack()5
6 return (7 <button8 type="button"9 onClick={() => track('checkout_started', { value: 99.0 })}10 >11 Proceed to checkout12 </button>13 )14}Identify a user
Call useIdentify after the user logs in to replace the anonymous context with an authenticated one.
1import { useIdentify } from '@avsbhq/react'2
3export function PostLoginCallback({ userId }: { userId: string }) {4 const identify = useIdentify()5 identify({ kind: 'user', key: userId })6 return null7}SSR and hydration
Unlike the App Router, the Pages Router runs getServerSideProps separately from the React tree. The integration pattern is:
- Fetch the datafile in
getServerSidePropsusinggetDatafile. - Pass it as
avsbBootstrapin the serialised page props. _app.tsxforwards it toAvsbProvidervia thebootstrapprop.- The provider evaluates flags synchronously from the bootstrap on the first render, matching the server-rendered HTML.
For pages that do not call getServerSideProps, the provider fetches the datafile asynchronously on mount. A brief loading state may appear. Wrap critical flag-gated UI in a Suspense boundary or use useFlagReady to show a skeleton until the client is initialised.
1import { useFlagReady } from '@avsbhq/react'2
3export function FlagGatedSection() {4 const ready = useFlagReady()5 if (!ready) return <Skeleton />6
7 return <ActualContent />8}Graceful shutdown
AvsbProvider calls client.close() on unmount. For custom Node servers, handle SIGTERM:
1process.on('SIGTERM', async () => {2 await server.close()3 process.exit(0)4})Testing
1import { AvsbTestProvider, TestData } from '@avsbhq/test'2import { render } from '@testing-library/react'3import { HeroSection } from './HeroSection'4
5const td = TestData.flag('homepage-hero').stringFlag().fallthroughVariation('variant-a')6
7render(8 <AvsbTestProvider flags={[td.build()]}>9 <HeroSection />10 </AvsbTestProvider>11)What's next
- Next.js App Router integration — if you are migrating to the App Router.
- Multi-context identity
@avsbhq/nextAPI reference@avsbhq/reactAPI reference