@flash-analytics/nextjs
v2.2.4
Published
If you want to test the Next.js SDK against local Flash Analytics services, point:
Readme
Flash Analytics Next.js SDK
Local development
If you want to test the Next.js SDK against local Flash Analytics services, point:
- the API endpoint to
http://localhost:3333 - the loader script to
http://localhost:3000/fa1.js
Example with the App Router:
import { FlashAnalyticsComponent } from '@flash-analytics/nextjs';
export default function RootLayout({
children,
}: Readonly<{ children: React.ReactNode }>) {
return (
<html lang="en">
<body>
<FlashAnalyticsComponent
appId="your-app-id"
endpoint="http://localhost:3333"
scriptUrl="http://localhost:3000/fa1.js"
maxSessionTimeoutInMin={3}
capturePageViews
captureErrors // js_error + unhandled_promise_rejection
captureVariants // auto-assign experiments on session + identify
deliveryStrategy={{
strategy: 'batching',
config: { size: 10, timeout: 5000 },
}}
/>
{children}
</body>
</html>
);
}SDK-local event rules and session timeout
<FlashAnalyticsComponent
appId="your-app-id"
maxSessionTimeoutInMin={3}
allowEvents={[
{
name: 'purchase',
allowProperties: ['amount', 'products.*.sku'],
blockProperties: ['email'],
},
]}
blockEvents={['debug.*']}
blockProperties={['internalDebug']}
/>Developer rules run before admin /sdk-config rules. allowEvents activates local whitelist mode when non-empty, while blockEvents drops matching events locally.
Property paths support products.*.sku for every array item and products.0.sku for only index 0.
maxSessionTimeoutInMin controls local session expiry and heartbeat timing, is sent to the backend, and is cached per client.
Example with the Pages Router:
import type { AppProps } from 'next/app';
import { FlashAnalyticsComponent } from '@flash-analytics/nextjs';
export default function App({ Component, pageProps }: AppProps) {
return (
<>
<FlashAnalyticsComponent
appId="your-app-id"
endpoint="http://localhost:3333"
scriptUrl="http://localhost:3000/fa1.js"
capturePageViews
/>
<Component {...pageProps} />
</>
);
}Batching
Enable batching to queue events in memory and send one /track/batch request when the queue reaches size or timeout expires. The browser SDK also flushes pending batches on pagehide and when the document becomes hidden, using fetch(..., { keepalive: true }).
<FlashAnalyticsComponent
appId="your-app-id"
deliveryStrategy={{
strategy: 'batching',
config: {
size: 10,
timeout: 5000,
},
}}
/>You can force-send pending batched events from client components:
'use client';
import { useFlashAnalytics } from '@flash-analytics/nextjs';
const analytics = useFlashAnalytics();
await analytics.flushBatch();For revenue-critical events, still send from your backend after the transaction succeeds.
If your app uses client helpers, import them normally:
'use client';
import { useFlashAnalytics } from '@flash-analytics/nextjs';
export function ExampleButton() {
const analytics = useFlashAnalytics();
return (
<button onClick={() => analytics.track('button_clicked')}>
Track
</button>
);
}Using the local package
If your Next.js app is outside this monorepo, build and link the local SDK packages first (run from the monorepo root):
pnpm --filter @flash-analytics/sdk build
pnpm --filter @flash-analytics/web build
pnpm --filter @flash-analytics/nextjs build
pnpm --filter @flash-analytics/sdk link --global
pnpm --filter @flash-analytics/web link --global
pnpm --filter @flash-analytics/nextjs link --globalThen in your Next.js app:
pnpm link --global @flash-analytics/sdk
pnpm link --global @flash-analytics/web
pnpm link --global @flash-analytics/nextjsWhen you change SDK code locally, rebuild the packages so your app picks up the latest output (from the monorepo root):
pnpm --filter @flash-analytics/sdk build
pnpm --filter @flash-analytics/web build
pnpm --filter @flash-analytics/nextjs buildRoute handler note
The Next.js route handler can proxy API requests to a custom endpoint:
import { createRouteHandler } from '@flash-analytics/nextjs/server';
export const { GET, POST } = createRouteHandler({
endpoint: 'http://localhost:3333',
});Use this only for API proxying. The built-in /fa1.js proxy still fetches the script from the production dashboard, so for local dashboard development you should set scriptUrl="http://localhost:3000/fa1.js" on FlashAnalyticsComponent.
Experiment Auto-Assignment
Add captureVariants to <FlashAnalyticsComponent> to automatically keep a local cache of experiment assignments in sync:
<FlashAnalyticsComponent
appId="your-app-id"
captureVariants // shorthand for captureVariants={true}
/>How the local cache is maintained
Session start (new session ID received from /track):
Fetches ALL experiments — profile, session, and event-triggered — and replaces the entire local store. Only fires when the session ID actually changes; repeated track() calls on the same session do not re-fetch.
After identify():
Fetches only profile-mode experiments (user identity just changed) and replaces only the profile-mode entries in the store. Session and event assignments are left untouched.
Session expiry: Removes only session-mode assignments when the cached session TTL runs out. Profile and event assignments survive.
Reading assignments
'use client';
import { useFlashAnalytics } from '@flash-analytics/nextjs';
export function PricingBanner() {
const analytics = useFlashAnalytics();
// All cached assignments — no API call
const all = analytics.getAllExperiments();
// Single experiment — checks cache first, falls back to API if not found
const assignment = await analytics.getExperimentById('checkout-cta');
console.log(assignment?.variantName);
}Local store state at each lifecycle point
| Moment | Profile assignments | Session assignments | Event assignments |
|---|---|---|---|
| After first track() (new session) | fetched | fetched | fetched |
| After identify() | re-fetched | unchanged | unchanged |
| After session expiry | unchanged | removed | unchanged |
| After next track() (new session) | re-fetched | re-fetched | re-fetched |
Session access
If you are using the browser helpers:
'use client';
import { useFlashAnalytics } from '@flash-analytics/nextjs';
const analytics = useFlashAnalytics();
const session = analytics.getSession();
console.log(session?.id);
console.log(session?.estimatedExpiresAt);
console.log(session?.estimatedTtlMs);This reads the current client-side session maintained by the underlying web SDK.
Cross-Platform Event Naming
With captureErrors on <FlashAnalyticsComponent>, Next.js emits the same event names as all other SDKs:
| Event | Trigger |
|---|---|
| js_error | Uncaught JS exception (captureErrors) |
| unhandled_promise_rejection | Unhandled promise rejection (captureErrors) |
| client_error (errorType: "react_render_error") | React component tree crash (<FlashErrorBoundary>) |
The legacy useAutoErrorTracking hook emits client_error (with errorType: "uncaught_exception" or "unhandled_rejection") instead. If you have existing queries filtering on client_error, migrate them to js_error / unhandled_promise_rejection after switching to captureErrors.
Automated Error Tracking
Add captureErrors to <FlashAnalyticsComponent> to automatically capture uncaught JS exceptions and unhandled promise rejections. These are sent as js_error and unhandled_promise_rejection events — the same event names used by all other Flash Analytics SDKs.
For React component tree crashes, wrap your layout with <FlashErrorBoundary>.
App Router (app/layout.tsx):
import { FlashAnalyticsComponent, FlashErrorBoundary } from '@flash-analytics/nextjs';
export default function RootLayout({ children }: { children: React.ReactNode }) {
const appId = process.env.NEXT_PUBLIC_FLASH_ANALYTICS_APP_ID!;
const endpoint = process.env.NEXT_PUBLIC_FLASH_ANALYTICS_ENDPOINT ?? 'https://api.flashanalytics.app';
return (
<html lang="en">
<body>
{/* captureErrors handles window/promise errors → js_error + unhandled_promise_rejection */}
<FlashAnalyticsComponent
appId={appId}
endpoint={endpoint}
capturePageViews
captureErrors
/>
{/* FlashErrorBoundary catches React render crashes → client_error with errorType: react_render_error */}
<FlashErrorBoundary
clientId={appId}
apiUrl={`${endpoint}/track`}
fallback={<h1>Something went wrong.</h1>}
>
{children}
</FlashErrorBoundary>
</body>
</html>
);
}Event names
| Error type | Event emitted | How |
|---|---|---|
| Uncaught JS exception | js_error | captureErrors on <FlashAnalyticsComponent> |
| Unhandled promise rejection | unhandled_promise_rejection | captureErrors on <FlashAnalyticsComponent> |
| React render crash | client_error (errorType: "react_render_error") | <FlashErrorBoundary> |
Legacy: useAutoErrorTracking
useAutoErrorTracking is still available but emits client_error (with errorType: "uncaught_exception" / "unhandled_rejection") rather than the cross-platform js_error / unhandled_promise_rejection event names. New apps should use captureErrors instead.
