@clickstream/react
Official React adapter. Wraps @clickstream/sdk + @clickstream/signals in React primitives so components can read visitor state and emit events without touching the underlying tracker instance.
Peer deps: react ^18 || ^19.
Runtime deps: @clickstream/sdk, @clickstream/signals.
Install
pnpm add @clickstream/react @clickstream/sdk @clickstream/signals
Provider setup
Wrap your app — usually in the top-level client component or the Next.js App Router root layout:
// app/providers.tsx
'use client';
import { ClickStreamProvider } from '@clickstream/react';
export function Providers({ children }: { children: React.ReactNode }) {
return (
<ClickStreamProvider
apiKey={process.env.NEXT_PUBLIC_CLICKSTREAM_KEY!}
endpoint="https://t.example.com"
>
{children}
</ClickStreamProvider>
);
}
Replace t.example.com with your first-party tracking domain. The endpoint is used by both the SDK and the signals client.
Hooks
useVisitor(): VisitorContext | null
Returns the current VisitorContext — bot classification, identity status, 11 behavioral scores, session + device summary. null while the signals endpoint warms up or when no cookie is set.
'use client';
import { useVisitor } from '@clickstream/react';
export function Pricing() {
const visitor = useVisitor();
if (!visitor) return <DefaultPricing />;
if (visitor.scores.intent >= 70) return <HighIntentOffer />;
if (visitor.scores.frustration >= 60) return <SupportPromo />;
return <DefaultPricing />;
}
The hook re-renders on every VisitorContext update — internally a 2-second poll of the signals endpoint. Pause the poll with <ClickStreamProvider pollIntervalMs={0}> if you want one-shot reads only.
useIdentify(): (email: string) => Promise<void>
Returns a callback that hashes the email client-side (SHA-256 + MD5) and sends an identify event.
'use client';
import { useIdentify } from '@clickstream/react';
export function LoginForm() {
const identify = useIdentify();
return (
<form onSubmit={async (e) => {
e.preventDefault();
const email = (e.currentTarget.elements.namedItem('email') as HTMLInputElement).value;
await identify(email);
}}>
<input name="email" type="email" />
<button type="submit">Sign in</button>
</form>
);
}
useTrack(): (event: TrackEventInput) => void
Returns a callback that fires a custom event.
'use client';
import { useTrack } from '@clickstream/react';
export function UpgradeCta() {
const track = useTrack();
return (
<button onClick={() => track({ name: 'upgrade_clicked', category: 'conversion' })}>
Upgrade
</button>
);
}
TrackEventInput matches CustomEvent from packages/shared-types (see Event schema).
useClickStream(): ClickStreamState
Low-level meta-hook. Returns { tracker, configured, error }. Prefer the narrower hooks for component code; reach for this one only when you need the raw tracker instance.
'use client';
import { useClickStream } from '@clickstream/react';
export function DebugPanel() {
const { tracker, configured, error } = useClickStream();
if (!configured) return <p>Loading…</p>;
if (error) return <p>Error: {error.message}</p>;
return <p>Visitor id: {tracker?.visitorId}</p>;
}
Throws if called outside a <ClickStreamProvider>.
Provider props
| Prop | Type | Default | Notes |
|---|---|---|---|
apiKey | string | required | ClickStream API key. |
endpoint | string | required in practice | Your first-party tracking domain. |
pollIntervalMs | number | 2000 | How often useVisitor polls the signals endpoint. Floor 250. |
debug | boolean | false | Log init / flush failures. |
Server-side rendering
The React adapter is client-only ('use client'). Import it from a Client Component. For Next.js Server Components + Route Handlers, use @clickstream/next — it exposes getServerVisitor() which reads the first-party cookie server-side.
The two adapters are designed to coexist: the Next middleware pre-fetches the VisitorContext into a request header, the server helper returns a snapshot at render time, and the React provider keeps the client in sync for interaction events.
Bundle impact
@clickstream/react itself is ~4 KB gzipped. It imports @clickstream/signals (~6 KB gzipped) and @clickstream/sdk (42 KB gzipped full-featured, or 2 KB via /core). The React layer adds minimal overhead; most of the weight is the underlying tracker.
Migration
If you were using @clickstream/sdk directly in React, the migration is mechanical:
- import { IdentityTracker } from '@clickstream/sdk';
- const tracker = new IdentityTracker({ apiKey, endpoint });
- useEffect(() => { tracker.init(); return () => tracker.destroy(); }, []);
+ // In your root layout:
+ import { ClickStreamProvider } from '@clickstream/react';
+ <ClickStreamProvider apiKey={…} endpoint="https://t.example.com">…</ClickStreamProvider>
- tracker.trackEvent({ name: 'signup' });
+ const track = useTrack();
+ track({ name: 'signup' });
See also
- Next.js adapter — Server Component + middleware support
- Signals API — underlying
VisitorContextshape - Install — full install matrix