Set up Faro with Next.js ΒΆ
Set up Grafana Faro in a Next.js application using the App Router (the default since Next.js 13).
Faro is a browser-only SDK and cannot run in React Server Components. You need a 'use client' component that initializes Faro on the client side.
Prerequisites ΒΆ
- A Next.js application using the App Router, deployed on Nais
- Node.js and npm
Install ΒΆ
npm install @grafana/faro-web-sdkFor browser tracing (optional β connects frontend spans with backend traces):
npm install @grafana/faro-web-tracingCreate the Faro component ΒΆ
Create a client component that initializes Faro. Use useEffect to avoid running side effects during server-side rendering or React Strict Mode double-invocations.
// app/faro.tsx
'use client';
import { useEffect } from 'react';
import { faro, getWebInstrumentations, initializeFaro } from '@grafana/faro-web-sdk';
import { TracingInstrumentation } from '@grafana/faro-web-tracing';
export default function Faro({ collectorUrl }: { collectorUrl?: string }) {
useEffect(() => {
if (faro.api) return; // already initialized
try {
initializeFaro({
url: collectorUrl || 'https://telemetry.external.prod.test-nais.cloud.nais.io/collect',
paused: window.location.hostname === 'localhost',
app: {
name: 'my-app',
},
instrumentations: [
...getWebInstrumentations(),
new TracingInstrumentation(),
],
});
} catch (e) {
console.warn('Faro initialization failed', e);
}
}, [collectorUrl]);
return null;
}Add it to your root layout ΒΆ
Include the component in your root app/layout.tsx so Faro initializes on every page. Pass the collector URL from the server environment:
// app/layout.tsx
import Faro from './faro';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="nb">
<body>
<Faro collectorUrl={process.env.NAIS_FRONTEND_TELEMETRY_COLLECTOR_URL} />
{children}
</body>
</html>
);
}The NAIS_FRONTEND_TELEMETRY_COLLECTOR_URL environment variable is set automatically when you enable spec.frontend.generatedConfig in your nais.yaml. If the env var isn't set, the Faro component falls back to https://telemetry.external.prod.test-nais.cloud.nais.io/collect.
`NEXT_PUBLIC_` env vars are build-time only
Don't use NEXT_PUBLIC_ for the collector URL. Next.js inlines NEXT_PUBLIC_* variables at next build time, so they won't change per cluster at deploy time. Pass runtime values through Server Components as props instead.
Configure your nais.yaml ΒΆ
Enable auto-configuration to get the collector URL set per cluster:
spec:
frontend:
generatedConfig:
mountPath: /tmp/nais.jsThis sets NAIS_FRONTEND_TELEMETRY_COLLECTOR_URL in your pod, which the root layout passes to the Faro component. The mountPath can be any writable path β for SSR apps the file itself isn't served to browsers, only the env var matters.
See the auto-configuration reference for all generated values.
Error boundaries ΒΆ
Next.js App Router has built-in error boundaries via error.tsx and global-error.tsx. Connect these to Faro to capture React rendering errors:
// app/error.tsx
'use client';
import { faro } from '@grafana/faro-web-sdk';
import { useEffect } from 'react';
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
useEffect(() => {
faro.api?.pushError(error);
}, [error]);
return (
<div>
<h2>Something went wrong</h2>
<button onClick={reset}>Try again</button>
</div>
);
}Create a similar app/global-error.tsx to catch errors in the root layout itself.
React integration package ΒΆ
The @grafana/faro-react package provides:
-
<FaroErrorBoundary>β wraps components and reports rendering errors to Faro -
<FaroRoutes>β tracks route changes as navigation events (for React Router) - React Router v7 helpers β
createReactRouterV7OptionsandcreateReactRouterV7DataOptions(since v2.2.3) - React 19 support (since v2.1.0)
Install it:
npm install @grafana/faro-reactUse the error boundary to wrap parts of your component tree:
import { FaroErrorBoundary } from '@grafana/faro-react';
function MyPage() {
return (
<FaroErrorBoundary fallback={<p>Something went wrong</p>}>
<MyComponent />
</FaroErrorBoundary>
);
}Mount/unmount tracking
The profiler integration tracks component mounts and unmounts, which generates a lot of data. Only enable it for specific components you want to monitor.
Next.js webpack externals ΒΆ
If you use the nais.js auto-configuration, add this to your next.config.js:
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
webpack: (config) => {
config.externals.push('./nais.js');
return config;
},
};
module.exports = nextConfig;Real-world example ΒΆ
-
grafana/faro-nextjs-exampleβ official Grafana example with Next.js App Router
For React Router examples with Faro, see the main setup guide.