Set up Faro ¶

Add Grafana Faro to a frontend application running on Nais.

Prerequisites ¶

  • A frontend application deployed on Nais (GCP only; on-premises is not supported)
  • Node.js and npm

Install ¶

sh

If you want browser tracing (connects frontend spans with backend traces), also install:

sh

Bundle size

@grafana/faro-web-tracing adds ~500kB to your JavaScript bundle. Only include it if you need trace propagation.

Initialize Faro ¶

Initialize Faro as early as possible in your application so it captures all errors and page loads.

js

Use the same name and namespace as your Nais app

app.name and app.namespace must match metadata.name and metadata.namespace in your nais.yaml. Nais APM uses these fields to group frontend telemetry with your app — if they don't match, your frontend data appears as a separate service in the APM service list.

Auto-configuration

Instead of hardcoding the collector URL, you can let the platform generate it for you. See auto-configuration below or the reference page for details.

Setting app.version ¶

Setting a version lets you filter and compare metrics across deploys in Grafana. If you use auto-configuration, the version is extracted from your container image tag automatically.

For manual setup, inject the commit SHA from your CI pipeline:

js

Auto-configuration ¶

The platform can generate the collector URL and app metadata for you. This is the recommended approach for static frontends (nginx, CDN) since the URL is set per cluster — no separate config for dev and prod.

Add this to your nais.yaml:

yaml

This generates a JavaScript file at the specified path containing the collector URL, your app name (from metadata.name), and version (from your image tag). The environment variable NAIS_FRONTEND_TELEMETRY_COLLECTOR_URL is also set in your pod.

See the auto-configuration reference for the full list of generated values.

Step 1: Create a local nais.js fallback ¶

Create a nais.js file for local development. Nais replaces this file at deploy time with the real values.

js

Step 2: Import and use it ¶

js

Step 3: Exclude nais.js from your bundler ¶

The local fallback file must not be bundled into your production build. Exclude it in your bundler config:

js
js
js

Capture exceptions ¶

Console errors are captured automatically. To get full stack traces for caught exceptions, push them to Faro:

js

Stack traces from pushed errors are automatically deobfuscated if sourcemaps are available.

React error boundaries ¶

Use <FaroErrorBoundary> from @grafana/faro-react to catch React rendering errors. Without this, errors that happen during rendering are silently lost.

tsx

For Next.js, see the dedicated error boundary pattern using error.tsx.

Performance tuning ¶

Faro generates a lot of data by default. Use these options to control the volume:

Session sampling ¶

Only instrument a percentage of user sessions:

js

Disable console capture ¶

If your app is verbose with console output, disable automatic capture:

js

Filter console levels ¶

By default, Faro ignores console.debug, console.trace, and console.log. To change which levels are captured, use the top-level consoleInstrumentation config:

js

To capture all levels, pass an empty array: disabledLevels: [].

Group dynamic URLs in Per-Page Performance ¶

If your app has dynamic URL segments (IDs, UUIDs), the APM dashboard's "Per-Page Performance" table shows one row per unique URL instead of grouping them by route. Use generatePageId to normalize dynamic paths into a single route:

js

The function receives the browser Location object and returns a string that identifies the "page route". All events from URLs matching the same pattern are grouped together in the dashboard.

Use your router's route definitions

If you use React Router or Next.js, consider deriving the page ID from your route config rather than writing regex patterns manually. For example, with React Router v6:

js

Content Security Policy (CSP) ¶

If your application uses a Content Security Policy, add the collector endpoint to connect-src:

Plaintext

Without this, the browser blocks Faro's requests to the collector. See Troubleshooting for more details.

Privacy and sensitive data ¶

Faro captures console output, errors, and HTTP request URLs automatically. Make sure you don't leak sensitive data:

  • Never log fødselsnummer, tokens, passwords, or other PII to the console
  • Watch URLs — query parameters and path segments may contain identifiers
  • Watch form input — don't send user input as custom events without redacting

Use the beforeSend hook to filter or redact telemetry:

js

Local development ¶

Set paused: window.location.hostname === 'localhost' (shown in the init example above) to skip telemetry during local development.

For a full local observability stack, check out the tracing demo repository and run docker-compose up.

Verify it works ¶

  1. Deploy your application
  2. Open it in a browser and interact with it
  3. Open your app in Grafana APM and go to the Frontend tab to see Core Web Vitals
  4. Or query Loki directly in Grafana Explore:
logql

Real-world examples ¶

These navikt repositories use Faro with React Router:

Next steps ¶