@flash-analytics/nextjs
v2.2.0
Published
## Local development
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"
capturePageViews
captureErrors // js_error + unhandled_promise_rejection
captureVariants // auto-assign experiments on session + identify
/>
{children}
</body>
</html>
);
}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} />
</>
);
}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.
