Menu
Grafana Cloud

Quickstart setup for Next.js and Frontend Observability

Follow these steps to get started quickly with the Faro Web SDK and Grafana Cloud Frontend Observability:

  1. Install the Faro Web SDK
  2. Instrument your application
  3. Create a Frontend Observability application
  4. Observe the application in Frontend Observability

The Faro Web SDK is a highly configurable open source real user monitoring (RUM) library built on OpenTelemetry and integrating seamlessly with Grafana Cloud and Grafana Frontend Observability.

Next.js is a popular hybrid framework that you can instrument for Real User Monitoring with Grafana Cloud. By monitoring your Next.js app with Frontend Observability, you can achieve the following:

  • Monitor the performance of your site: collect performance KPIs such as Web Vitals automatically
  • Error capturing: leverage the automatic error capturing on the browser side and get line-level insights on error
  • End-to-end observability: automatically connect RUM sessions with APM and vice-versa for maximum insights on performance and root-causes

Example code for this guide

You can find an example application that’s already fully customized at https://github.com/grafana/faro-nextjs-example.

Install the Faro Web SDK

First, add the Faro Web SDK to your project. Install the Faro Web SDK package by running the following command for NPM:

sh
npm i @grafana/faro-web-sdk

Or the following command Yarn:

sh
yarn add @grafana/faro-web-sdk

Create a component in components/frontend-observability.tsx so you can inject the Web SDK into your Next.js frontend:

ts
'use client';

import { faro, getWebInstrumentations, initializeFaro } from '@grafana/faro-web-sdk';
import { TracingInstrumentation } from '@grafana/faro-web-tracing';

export default function FrontendObservability() {
  // skip if already initialized
  if (faro.api) {
    return null;
  }

  try {
    const faro = initializeFaro({
      url: process.env.NEXT_PUBLIC_FARO_URL,
      app: {
        name: process.env.NEXT_PUBLIC_FARO_APP_NAME || 'unknown_service:webjs',
        namespace: process.env.NEXT_PUBLIC_FARO_APP_NAMESPACE || undefined,
        version: process.env.VERCEL_DEPLOYMENT_ID || '1.0.0',
        environment: process.env.NEXT_PUBLIC_VERCEL_ENV || 'development',
      },

      instrumentations: [
        // Mandatory, omits default instrumentations otherwise.
        ...getWebInstrumentations(),

        // Tracing package to get end-to-end visibility for HTTP requests.
        new TracingInstrumentation(),
      ],
    });
  } catch (e) {
    return null;
  }
  return null;
}

The Faro Web SDK captures data about your application’s health and performance and exports it to a managed Grafana Cloud endpoint.

Open source users can use the Grafana Alloy as their data collector. See the Faro receiver with Grafana Alloy documentation to learn more.

Enable backend correlation

To allow Frontend Observability in Grafana Cloud to match client side telemetry with server-side telemetry, create a middleware.ts in the root of your project. In the middleware, which is executed on the server side, send a server-timing header to allow the client-side performance KPIs to be matched with the exact requests recorded on the backend side.

ts
import { NextRequest, NextResponse } from 'next/server';
import { trace } from '@opentelemetry/api';

export function middleware(request: NextRequest) {
  const response = NextResponse.next();
  const current = trace.getActiveSpan();

  // set server-timing header with traceparent
  if (current) {
    response.headers.set(
      'server-timing',
      `traceparent;desc="00-${current.spanContext().traceId}-${current.spanContext().spanId}-01"`
    );
  }
  return response;
}

Instrument the Next.js backend

To get the full picture, you should also instrument the backend parts of your Next.js app.

This allows you to correlate with backend activity from the frontend and create a better understanding and broader set of signals when investigating an issue.

Install the required packages:

bash
npm install @vercel/otel @opentelemetry/sdk-logs @opentelemetry/api-logs @opentelemetry/instrumentation

Create a new file instrumentation.ts in the root of your project. This file contains the configuration for the OpenTelemetry instrumentation for the Node.js backend of your app.

It contains a few defaults and a custom span processor to reduce the cardinality of span names.

ts
import { Context } from '@opentelemetry/api';
import { ReadableSpan, Span, SpanProcessor } from '@opentelemetry/sdk-trace-node';
import { registerOTel } from '@vercel/otel';

/**
 * Span processor to reduce cardinality of span names.
 *
 * Customize with care!
 */
class SpanNameProcessor implements SpanProcessor {
  forceFlush(): Promise<void> {
    return Promise.resolve();
  }
  onStart(span: Span, parentContext: Context): void {
    if (span.name.startsWith('GET /_next/static')) {
      span.updateName('GET /_next/static');
    } else if (span.name.startsWith('GET /_next/data')) {
      span.updateName('GET /_next/data');
    }
  }
  onEnd(span: ReadableSpan): void {}
  shutdown(): Promise<void> {
    return Promise.resolve();
  }
}

export function register() {
  registerOTel({
    serviceName: process.env.OTEL_SERVICE_NAME || 'unknown_service:node',
    spanProcessors: ['auto', new SpanNameProcessor()],
  });
}

Note

For even more insights, take a look at @vercel/otel to learn how you can include more instrumentation, such as automatic tracing for databases and other important software.

Connect frontend (RUM) and backend (APM) insights

Create a middleware.ts file in the root of your app to enable strong correlation between frontend and backend spans. This middleware extracts the trace context from the incoming request and injects it into the response headers.

typescript
import { NextRequest, NextResponse } from 'next/server';
import { trace } from '@opentelemetry/api';

export function middleware(request: NextRequest) {
  const response = NextResponse.next();
  const current = trace.getActiveSpan();

  // set server-timing header with traceparent
  if (current) {
    response.headers.set(
      'server-timing',
      `traceparent;desc="00-${current.spanContext().traceId}-${current.spanContext().spanId}-01"`
    );
  }
  return response;
}

Enable Client service mapping in Application Observability

In your Grafana Cloud stack, enable client services. This ensures that frontend services such as your Next.js app are included in the service graph.

Set environment variables to configure the instrumentation

Next, provide the necessary environment variables to your app to configure the new instrumentation.

## The configuration for the frontend observability instrumentation
## The URL of your Faro instance, can be found in the Grafana Cloud UI
NEXT_PUBLIC_FARO_URL=my-url
## The name of your app, can be found in the Grafana Cloud UI; should be different for your backend and frontend
NEXT_PUBLIC_FARO_APP_NAME=next-frontend
## The namespace of your app, can be chosen by you and should optimally be the same as the namespace of your backend
NEXT_PUBLIC_FARO_APP_NAMESPACE=nextjs-example

# Next App BACKEND Instrumentation
## Example assumes that the collector is running on the same machine
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
## Force protobuf
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
## Set Backend service name
OTEL_SERVICE_NAME=next-backend
## Customize resource attributes, namespace is a recommended attribute, here we set it to the same value as the frontend namespace to enable correlation
OTEL_RESOURCE_ATTRIBUTES=service.namespace=nextjs-example

Create a Frontend Observability application

To observe Real User Monitoring (RUM) data in Grafana Cloud Frontend Observability create an application in the Grafana Cloud Frontend Observability plugin:

  1. Sign in to Grafana Cloud, register for a Free Grafana Cloud account if required, to get to the Grafana Cloud Portal page.

    If the account has access to multiple Grafana Cloud Organizations, select an organization from the top left organization drop-down.

    If the organization has access to multiple Grafana Cloud Stacks, navigate to a stack from the left side bar or the main Stacks list.

  2. With a stack selected, or in the single stack scenario, below Manage your Grafana Cloud Stack, click Launch in the Grafana section:

    The Launch button that opens Grafana Cloud

  3. Use the left navigation to expand Frontend and select Frontend Apps.

    If this is the first time you access Frontend Observability it displays the landing page. Click Start observing on the landing page to navigate to the overview page.

  4. From the overview page, click Create new and to create a Frontend Observability application to hold RUM data for your web application.

    To create an application requires the following information:

    • App Name: Give your app a meaningful name
    • CORS Allowed Origin: Domains allowed to access your Frontend Observability application, for local development set the value to localhost
    • Default attributes: Any attributed you’d like added to all signals, if unknown leave empty
  5. Connect your instrumentation to Frontend Observability, from the Web SDK Configuration tab, copy the url value and add it to your instrumentation script to tell Faro which application to send data to. Make use of the CORS domain to secure your Frontend Observability application to specific domains.

Observe the application in Frontend Observability

Run your web application and make some requests to instrumented pages to send data to Grafana Cloud.

Learn how to explore your application in Frontend Observability with the documentation: