Skip to content

Integrating under SSR Framework

The RUM SDK can only be initialized in the browser environment and must not be executed during server-side rendering. This page describes how to integrate the official framework plugins for Next.js and Nuxt.

Stage Runtime Environment Initialize RUM
SSR, Server Component, or Nitro Execution Phase Node.js No
Hydration and Client-Side Routing Phase Browser Yes

The framework plugins capture browser Views, Resources, Actions, and client-side errors. For pure server-side requests, Server Actions, Nitro processing, and server-side errors that are not passed to the client, a server-side monitoring solution is required.

Version Requirements

Next.js and Nuxt framework plugins are available starting from RUM SDK 3.3.6. The version of the main RUM package must not be lower than the framework plugin version.

The following examples use the public OpenWay site and clientToken. When using a direct connection to DataKit, replace these two parameters with datakitOrigin; do not configure both reporting endpoints simultaneously.

Next.js

The Next.js plugin supports Next.js 13 and above, React 18 and above, as well as App Router and Pages Router.

Installation

npm install @cloudcare/browser-rum @cloudcare/browser-rum-nextjs

App Router: Next.js 15.3 and above

Next.js 15.3 and above support instrumentation-client.js|ts. This file runs before hydration, making it suitable for initializing browser monitoring as early as possible.

Create instrumentation-client.ts; when using the src directory, place the file in src/instrumentation-client.ts:

import { datafluxRum } from "@cloudcare/browser-rum"
import {
  nextjsPlugin,
  onRouterTransitionStart,
} from "@cloudcare/browser-rum-nextjs"

export { onRouterTransitionStart }

datafluxRum.init({
  applicationId: "<APPLICATION_ID>",
  site: "<PUBLIC_OPENWAY_URL>",
  clientToken: "<CLIENT_TOKEN>",
  service: "web-nextjs",
  env: "production",
  version: "1.0.0",
  sessionSampleRate: 100,
  plugins: [nextjsPlugin()],
})

Create a client-side Router tracker:

// app/rum-router-tracker.tsx
"use client"

import { RumNextjsAppRouter } from "@cloudcare/browser-rum-nextjs"

export function RumRouterTracker() {
  return <RumNextjsAppRouter />
}

Render it once in the root layout:

// app/layout.tsx
import { RumRouterTracker } from "./rum-router-tracker"

export default function RootLayout({ children }) {
  return (
    <html lang="zh-CN">
      <body>
        <RumRouterTracker />
        {children}
      </body>
    </html>
  )
}

onRouterTransitionStart() records the navigation target, and RumNextjsAppRouter only creates a View after the new pathname is actually committed. Invalid Views are not created for canceled, failed, or uncommitted renderings; when a successful redirect occurs, the View uses the final committed pathname.

App Router: Next.js 13 to 15.2

These versions do not have instrumentation-client. You can place a client-side initialization component in the root layout:

// app/rum-provider.tsx
"use client"

import { useEffect } from "react"
import { datafluxRum } from "@cloudcare/browser-rum"
import {
  nextjsPlugin,
  RumNextjsAppRouter,
} from "@cloudcare/browser-rum-nextjs"

let initialized = false

export function RumProvider() {
  useEffect(() => {
    if (initialized) return
    initialized = true

    datafluxRum.init({
      applicationId: "<APPLICATION_ID>",
      site: "<PUBLIC_OPENWAY_URL>",
      clientToken: "<CLIENT_TOKEN>",
      service: "web-nextjs",
      env: "production",
      version: "1.0.0",
      sessionSampleRate: 100,
      plugins: [nextjsPlugin()],
    })
  }, [])

  return <RumNextjsAppRouter />
}

Render <RumProvider /> in app/layout.tsx. This approach can capture successfully committed App Router routes, but the initialization timing is later than instrumentation-client.

Pages Router

Initialize and render the Pages Router tracker in pages/_app.tsx:

import { useEffect } from "react"
import { datafluxRum } from "@cloudcare/browser-rum"
import {
  nextjsPlugin,
  RumNextjsPagesRouter,
} from "@cloudcare/browser-rum-nextjs"

let initialized = false

export default function App({ Component, pageProps }) {
  useEffect(() => {
    if (initialized) return
    initialized = true

    datafluxRum.init({
      applicationId: "<APPLICATION_ID>",
      site: "<PUBLIC_OPENWAY_URL>",
      clientToken: "<CLIENT_TOKEN>",
      service: "web-nextjs",
      env: "production",
      version: "1.0.0",
      sessionSampleRate: 100,
      plugins: [nextjsPlugin()],
    })
  }, [])

  return (
    <>
      <RumNextjsPagesRouter />
      <Component {...pageProps} />
    </>
  )
}

Pages Router uses router.pathname as the template name. For example, the actual URL /users/42 is grouped under /users/[id]. routeChangeError, canceled navigation, and changes to only query or hash do not create a new View.

Next.js View and Error

App Router generates file route templates based on usePathname() and useParams():

Actual URL View Name
/ /
/users/42 /users/[id]
/docs/a/b /docs/[...slug]

When a stable name cannot be automatically generated, you can pass getViewName:

<RumNextjsAppRouter
  getViewName={(pathname, params) => (
    params.id ? "/users/[id]" : pathname
  )}
/>

Call addNextjsError() in error.tsx, global-error.tsx, or business error handlers of App Router:

"use client"

import { useEffect } from "react"
import { addNextjsError } from "@cloudcare/browser-rum-nextjs"

export default function ErrorPage({ error, reset }) {
  useEffect(() => {
    addNextjsError(error, undefined, {
      route_boundary: "dashboard",
    })
  }, [error])

  return <button onClick={reset}>Retry</button>
}

When the error object includes a Next.js digest, the plugin writes it into the Error context, making it easier to correlate with server-side logs. The plugin also provides an ErrorBoundary that can be used to protect client-side React subtrees.

Nuxt

The Nuxt plugin supports Nuxt 3, 4, as well as Vue 3 and Vue Router 4.

Installation

npm install @cloudcare/browser-rum @cloudcare/browser-rum-nuxt

Create a Client Plugin

Create rum.client.ts in the project's plugins directory. The .client suffix ensures the code only runs in the browser, and enforce: "pre" makes RUM install route and error listeners as early as possible.

import { datafluxRum } from "@cloudcare/browser-rum"
import { nuxtRumPlugin } from "@cloudcare/browser-rum-nuxt"

export default defineNuxtPlugin({
  name: "dataflux-rum",
  enforce: "pre",
  setup(nuxtApp) {
    datafluxRum.init({
      applicationId: "<APPLICATION_ID>",
      site: "<PUBLIC_OPENWAY_URL>",
      clientToken: "<CLIENT_TOKEN>",
      service: "web-nuxt",
      env: "production",
      version: "1.0.0",
      sessionSampleRate: 100,
      plugins: [
        nuxtRumPlugin({
          nuxtApp,
          router: useRouter(),
        }),
      ],
    })
  },
})

Nuxt automatically registers plugins at the top level of the plugins directory; no need to write in nuxt.config.ts. Do not use both the Nuxt plugin and the Vue plugin to track the same Router in the same application.

Nuxt View and Error

The plugin converts Vue Router parameter paths to Nuxt file route names:

Vue Router Path RUM View Name
/users/:id /users/[id]
/users/:id? /users/[[id]]
/docs/:slug(.*)* /docs/[...slug]

Successful pathname navigations and hash routes create a View; only query changes and failed navigations do not create a View. To customize the name, pass getViewName(route) in nuxtRumPlugin().

When nuxtApp is passed, the plugin handles both Vue component errors and Nuxt app:error. If the same Error object enters both error paths during a single propagation, it will only be reported once, preserving the existing Vue error handler of the application.

Call addNuxtError() when actively catching errors in business logic:

import { addNuxtError } from "@cloudcare/browser-rum-nuxt"

try {
  await submitOrder()
} catch (error) {
  addNuxtError(error, {
    operation: "submit_order",
    module: "checkout",
  })
}

Verify the Integration

  1. Open the browser developer tools and filter by /v1/write/rum in the Network tab.
  2. On the first page load, confirm that type=view appears.
  3. Navigate to a dynamic route, confirm that the View uses a file route template like /users/[id].
  4. Change only the query parameter, confirm that no duplicate View is created.
  5. Trigger a client component error, confirm that type=error appears, and includes context.framework = nextjs or context.framework = nuxt.

Frequently Asked Questions

window is not defined during build

RUM initialization has entered a server-side module. For Next.js, use instrumentation-client.ts or an initialization component with "use client"; for Nuxt, the plugin file must use the .client.ts or .client.js suffix.

Error exists but no framework View

For Next.js, the tracker corresponding to the current Router must be rendered; for Nuxt, the plugin must receive router: useRouter(). Do not mix multiple Router trackers in the same application.

One navigation produces multiple Views

Place only one tracker in the entire application, and do not call datafluxRum.startView() for the same navigation.

Feedback

Is this page helpful? ×