React SDK
Provider and hooks that wrap @getfluxly/browser for React 18+ and any React-based framework (Next.js, Remix, TanStack Start). SSR-safe: all hooks return no-ops during SSR, and the provider's effects never touch window on the server.
Install
npm install @getfluxly/react @getfluxly/browser react
@getfluxly/browser is a peer dependency. Install it alongside @getfluxly/react.
Provider setup
Wrap your app tree in GFluxProvider once. In Next.js App Router, you must place this inside a "use client" boundary. The recommended pattern is a dedicated providers.tsx wrapper:
// app/providers.tsx
"use client";
import { GFluxProvider } from "@getfluxly/react";
export function Providers({ children }: { children: React.ReactNode }) {
return (
<GFluxProvider apiKey={process.env.NEXT_PUBLIC_GFLUX_API_KEY!}>
{children}
</GFluxProvider>
);
}
Then use it in your root layout (which can remain a Server Component):
// app/layout.tsx
import { Providers } from "./providers";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}
Note:
apiKeyis captured at first mount. Swapping it later warns in dev and is ignored. Remount the provider to switch keys.
Quick start
// app/sign-up-button.tsx
"use client";
import { useTrack, useIdentify } from "@getfluxly/react";
export function SignUpButton() {
const track = useTrack();
const identify = useIdentify();
return (
<button onClick={async () => {
const user = await signUp();
identify(user.id, { email: user.email, plan: user.plan });
track("user_signed_up", { plan: user.plan });
}}>
Sign up
</button>
);
}
Hooks
| Hook | Returns | Notes |
|---|---|---|
useTrack<TEvents>() | (name, props?) => void | Generic type parameter enables typed events and IDE autocomplete |
useIdentify() | (externalId, traits?) => void | Second call with a different externalId is dropped with a warn log |
usePage() | (properties?) => void | Manual page view. Most apps do not need this |
useConsent() | { status, optIn, optOut } | status is "unknown" / "opted_in" / "opted_out" |
useGFlux() | GFluxBrowser | null | Raw SDK instance, or null during SSR / before mount |
All hooks return stable references and are safe to place in useEffect dependency arrays.
Hook reference
useTrack
type AppEvents = {
user_signed_up: { plan: "free" | "pro" };
feature_used: { feature_name: string; ms_to_first_use: number };
};
const track = useTrack<AppEvents>();
track("user_signed_up", { plan: "pro" }); // valid
track("user_signed_up", { plan: "gold" }); // type error
track("typo_event", {}); // unknown event: type error
Without the generic, track accepts any string name and any properties object.
useIdentify
Returns a stable identify callback. Calling identify twice with different externalId values in the same session is a bug in identity stitching. The second call is dropped client-side and a warn log is emitted. To re-identify, call reset() via useGFlux() first.
usePage
Returns a stable page callback for manual page view events. Most apps do not need to call this explicitly because the browser SDK autocaptures SPA route changes. Call it only when you want a custom page view with extra properties:
const page = usePage();
page({ section: "billing", plan: "pro" });
useConsent
const { status, optIn, optOut } = useConsent();
status values:
| Value | Meaning |
|---|---|
"unknown" | SSR path, or EU implicit-pending state (events buffered until optIn() is called) |
"opted_in" | User has consented; events flow normally |
"opted_out" | User has declined; events are suppressed |
useGFlux
Returns the raw GFluxBrowser instance, or null during SSR and before the provider has mounted. Use this for methods that do not have a dedicated hook:
const gflux = useGFlux();
// Reset anonymous ID and drop external identity (e.g. on sign-out)
gflux?.reset();
// Tear down the client entirely
gflux?.destroy();
// Inspect internal counters and queued events
gflux?.__debug();
Provider props
| Prop | Type | Notes |
|---|---|---|
apiKey | string | Publishable key (gflux_pub_*). Required unless client is supplied. |
apiHost | string | Defaults to https://api.getfluxly.com |
config | Partial<GFluxConfig> | Forwarded to initGFlux (autocapture toggles, consentMode, region, flushIntervalMs, etc.) |
client | GFluxBrowser | null | Inject a pre-constructed instance. When set, apiKey/apiHost/config are ignored. Useful for tests. |
If both apiKey and client are omitted, the provider renders children and every hook returns a no-op. This is useful for environments where analytics is intentionally disabled.
SSR and Server Components
All hooks are SSR-safe:
- During SSR (no
window),useGFlux()returnsnullanduseTrack/useIdentify/usePagereturn no-op functions. - The provider wraps
initGFluxinuseEffect, so the browser SDK only boots on the client. - Every module in
@getfluxly/reactthat touches React state ships its own"use client"directive. You do not need to add one yourself just to use the hooks.
Errors
track, identify, and page are fire-and-forget and never throw to caller code. Errors land in the configured logger (defaults to console.warn) and are reflected in __debug() counters.
The provider throws at construction if apiKey is malformed. This surfaces as a render error in development immediately.
Privacy and consent
Consent behavior (GPC, EU buffering, explicit opt-in/out) is inherited entirely from @getfluxly/browser. The React wrapper does not add or change any privacy logic. See the @getfluxly/browser docs for the full posture.
Use with Next.js
The providers.tsx pattern above is the recommended approach for App Router. If you prefer a more integrated setup, @getfluxly/next ships a Next-aware provider with automatic route-change tracking. See the Next SDK.