Next.js example
Wire up one canonical anonymous id and fan a single identify() call out to
Segment, LaunchDarkly, and FullStory — from a Next.js App Router app. See
Architecture for the concepts behind
IdentityProvider/IdentityClient.
1. Install
npm install @idhub/identity-core @idhub/identity-adapter-nextjs \
@idhub/identity-provider-segment @idhub/identity-provider-launchdarkly @idhub/identity-provider-fullstory
2. middleware.ts — guarantee the anonymous id before first render
// middleware.ts
import { identityMiddleware } from '@idhub/identity-adapter-nextjs/middleware'
export const middleware = identityMiddleware()
export const config = { matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'] }
This is the whole file for apps with no middleware logic of their own. It
guarantees the _anon_id cookie exists — and is visible to Server Components
rendering this same request — on the very first hit, cold cache included.
If you already have middleware logic (auth redirects, rewrites), compose with
withIdentity instead, which hands back the pieces rather than wrapping your
handler:
// middleware.ts
import { withIdentity, applyAnonymousIdCookie } from '@idhub/identity-adapter-nextjs/middleware'
import { NextResponse, type NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
const { response, anonymousId } = withIdentity(request)
response.headers.set('x-anon-id', anonymousId)
return response
}
export const config = { matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'] }
3. app/layout.tsx — read the id on the server, mount the client providers
// app/layout.tsx — Server Component
import { getAnonymousIdFromCookies } from '@idhub/identity-adapter-nextjs/server'
import { cookies } from 'next/headers'
import { Providers } from './providers'
export default async function RootLayout({ children }: { children: React.ReactNode }) {
const anonymousId = getAnonymousIdFromCookies(await cookies())
return (
<html lang="en">
<body>
<Providers initialAnonymousId={anonymousId}>{children}</Providers>
</body>
</html>
)
}
// app/providers.tsx
'use client'
import { IdentityProvider } from '@idhub/identity-adapter-nextjs'
import { segmentProvider } from '@idhub/identity-provider-segment'
import { launchDarklyProvider } from '@idhub/identity-provider-launchdarkly'
import { fullStoryProvider } from '@idhub/identity-provider-fullstory'
import { analytics } from '../lib/segment-client' // your own analytics-node / analytics.js instance
export function Providers({
initialAnonymousId,
children,
}: {
initialAnonymousId?: string
children: React.ReactNode
}) {
return (
<IdentityProvider
providers={[
segmentProvider({ client: analytics, writeKey: process.env.NEXT_PUBLIC_SEGMENT_WRITE_KEY! }),
launchDarklyProvider({ clientSideId: process.env.NEXT_PUBLIC_LD_CLIENT_ID! }),
fullStoryProvider({ orgId: process.env.NEXT_PUBLIC_FULLSTORY_ORG_ID! }),
]}
initialAnonymousId={initialAnonymousId}
>
{children}
</IdentityProvider>
)
}
initialAnonymousId is passed explicitly from the server read in step 3
rather than derived from document.cookie on first render — <IdentityProvider>
is a Client Component that Next.js still server-renders, and there is no
document on the server, so a cookie-derived initial value would mint a second
id that disagrees with the one the browser holds. Passing the server value down
keeps the SSR pass and the hydration pass in agreement.
4. Call identify() at login
'use client'
import { useIdentity } from '@idhub/identity-adapter-nextjs'
export function LoginButton() {
const { state, identify, reset } = useIdentity()
return (
<button onClick={() => identify('user-123', { plan: 'pro' })}>
{state.status === 'identified' ? `Hi ${state.userId}` : 'Log in'}
</button>
)
}
One call fans out to onIdentify on all three providers — Segment
(identify(userId, traits)), LaunchDarkly (multi-kind device + user
context, replacing the removed alias()), and FullStory (setIdentity,
which auto-merges the anonymous session). Call reset() on logout; it issues
a brand-new anonymous id rather than reusing the pre-login one.
That's the full integration: one middleware export, one provider composition
in app/layout.tsx/app/providers.tsx, and useIdentity() anywhere you need
identify/reset/track/page.
Building an Angular app instead? See the Angular example.