The browser agent works in any web application on its own. The framework packages add two things on top: they name views by route pattern instead of resolved URL, and they catch errors the framework would otherwise swallow.
Each section on this page is a complete setup for one framework, from installing the package through to reporting errors. Follow the section that matches your application and skip the rest.
Before you start
You need three things:
- An Atatus RUM application and its license key.
- A build tool that resolves npm packages, such as Vite, webpack, or your framework's own CLI.
- A framework version the package supports, listed in the table below.
If you have not installed the base agent yet, start with Installation. The framework packages sit alongside @atatus/browser-rum, they do not replace it.
Choose your package
Install the package that matches your framework, alongside the base agent:
| Framework | Package | Requires |
|---|---|---|
| React | @atatus/browser-rum-react |
React 18 or 19. React Router 6, 7, or 8, or TanStack Router 1.64+ |
| Angular | @atatus/browser-rum-angular |
Angular 15 through 22, RxJS 7+ |
| Vue | @atatus/browser-rum-vue |
Vue 3.5+, Vue Router 4 or 5 for route tracking |
| Next.js | @atatus/browser-rum-nextjs |
Next.js 15.3+, React 18+ |
| Nuxt | @atatus/browser-rum-nuxt |
Nuxt 3 or 4, Vue 3.5+, Vue Router 4+ |
The Next.js package needs 15.3 or later because it relies on the instrumentation-client file convention, which earlier versions do not have. If you are on an older Next.js, use the React package and name views from your own navigation hook.
Why view naming matters
Without a router integration, a view is named by the URL the user landed on. In an application with IDs in the path, /accounts/8f21/orders/4417 and /accounts/6b02/orders/9910 look like two unrelated pages, and neither has enough traffic to show a trend.
With the router integration, both report as /accounts/:accountId/orders/:orderId, so they aggregate into one view you can measure.
Turning on the router option switches the agent to manual view tracking. If you enable it but keep importing your router from the framework instead of from the Atatus package, the agent records no views at all, and nothing warns you. Enable router and swap the imports together, or leave router off.
React
The React package names views from your route tree and reports render errors through an error boundary.
1. Install the package
Install it alongside the base agent:
$ npm install @atatus/browser-rum @atatus/browser-rum-react
2. Initialize the agent with the plugin
Add reactPlugin to plugins where you initialize the agent, before your app renders:
import { atatusRum } from '@atatus/browser-rum';
import { reactPlugin } from '@atatus/browser-rum-react';
atatusRum.init({
licenseKey: '<license-key>',
appName: 'Storefront',
env: 'production',
sessionSampleRate: 100,
sessionReplaySampleRate: 30,
trackUserInteractions: true,
plugins: [reactPlugin({ router: true })],
});
3. Import your router from the plugin
With router: true, import the router pieces from the plugin instead of from React Router. Pick the entry that matches your router:
| Router | Import from |
|---|---|
| React Router 6 | @atatus/browser-rum-react/react-router-v6 |
| React Router 7 | @atatus/browser-rum-react/react-router-v7 |
| React Router 8 | @atatus/browser-rum-react/react-router-v8 |
| TanStack Router | @atatus/browser-rum-react/tanstack-router |
For a data router, swap the router factory:
import { RouterProvider } from 'react-router';
import { createBrowserRouter } from '@atatus/browser-rum-react/react-router-v7';
const router = createBrowserRouter([
{ path: '/', element: <Root /> },
{ path: '/orders/:orderId', element: <Order /> },
]);
ReactDOM.createRoot(document.getElementById('root')).render(<RouterProvider router={router} />);
For a declarative router, swap the Routes component and leave your <Route> elements alone:
import { BrowserRouter } from 'react-router-dom';
import { Routes } from '@atatus/browser-rum-react/react-router-v7';
<BrowserRouter>
<Routes>
{/* your <Route> elements, unchanged */}
</Routes>
</BrowserRouter>
Each React Router entry also exports createHashRouter, createMemoryRouter, and useRoutes. The TanStack entry exports createRouter:
import { createRouter } from '@atatus/browser-rum-react/tanstack-router';
const router = createRouter({ routeTree });
The v6 entry re-exports from react-router-dom, while the v7 and v8 entries re-export from react-router. Match the entry to the package your application already depends on.
4. Catch render errors
Wrap the part of your tree you want protected in ErrorBoundary. It reports the error with its component stack, then renders your fallback:
import { ErrorBoundary } from '@atatus/browser-rum-react';
<ErrorBoundary fallback={({ error, resetError }) => (
<div>
<p>Something went wrong</p>
<button onClick={resetError}>Try again</button>
</div>
)}>
<App />
</ErrorBoundary>
If you already have your own error boundary, keep it and report from inside it with addReactError:
import { addReactError } from '@atatus/browser-rum-react';
componentDidCatch(error, info) {
addReactError(error, info);
}
Reference: React Router, TanStack Router, and Error Tracking.
Angular
The Angular package names views from the matched route and replaces Angular's error handler with one that reports to Atatus.
1. Install the package
Install it alongside the base agent:
$ npm install @atatus/browser-rum @atatus/browser-rum-angular
2. Initialize the agent with the plugin
Initialize the agent before you bootstrap the application, usually in main.ts:
import { atatusRum } from '@atatus/browser-rum';
import { angularPlugin } from '@atatus/browser-rum-angular';
atatusRum.init({
licenseKey: '<license-key>',
appName: 'Storefront',
env: 'production',
sessionSampleRate: 100,
trackUserInteractions: true,
plugins: [angularPlugin({ router: true })],
});
3. Track route changes
Add provideAtatusRouter() to your providers. For a standalone application:
import { bootstrapApplication } from '@angular/platform-browser';
import { provideRouter } from '@angular/router';
import { provideAtatusRouter } from '@atatus/browser-rum-angular';
bootstrapApplication(AppComponent, {
providers: [provideRouter(routes), provideAtatusRouter()],
});
For an NgModule application, add it to the module providers instead:
import { provideAtatusRouter } from '@atatus/browser-rum-angular';
@NgModule({
providers: [provideAtatusRouter()],
})
export class AppModule {}
The provider starts a new view when route guards pass and before resolvers run, so data your resolvers fetch is attributed to the view that asked for it.
4. Report component errors
Add provideAtatusErrorHandler() to report errors Angular would otherwise only log to the console. It keeps the default console.error behavior:
import { bootstrapApplication } from '@angular/platform-browser';
import { provideAtatusErrorHandler } from '@atatus/browser-rum-angular';
bootstrapApplication(AppComponent, {
providers: [provideAtatusErrorHandler()],
});
If you already have a custom ErrorHandler, keep it and call addAngularError from inside:
import { ErrorHandler } from '@angular/core';
import { addAngularError } from '@atatus/browser-rum-angular';
class MyErrorHandler implements ErrorHandler {
handleError(error: unknown): void {
addAngularError(error);
// your own handling, such as showing a toast
}
}
Reference: Angular Router and Error Tracking.
Vue
The Vue package names views from Vue Router and reports component errors with their component stack.
1. Install the package
Install it alongside the base agent:
$ npm install @atatus/browser-rum @atatus/browser-rum-vue
2. Initialize the agent with the plugin
Initialize the agent in main.ts or main.js:
import { atatusRum } from '@atatus/browser-rum';
import { vuePlugin } from '@atatus/browser-rum-vue';
atatusRum.init({
licenseKey: '<license-key>',
appName: 'Storefront',
env: 'production',
sessionSampleRate: 100,
trackUserInteractions: true,
plugins: [vuePlugin({ router: true })],
});
3. Create your router with the Atatus wrapper
Import createRouter from the plugin's router entry instead of from vue-router, and keep the rest of your router options unchanged:
import { createWebHistory } from 'vue-router';
import { createRouter } from '@atatus/browser-rum-vue/vue-router-v4';
const router = createRouter({
history: createWebHistory(),
routes: [
{ path: '/', component: Home },
{ path: '/orders/:orderId', component: Order },
],
});
The wrapper skips failed navigations and query-only changes, so a filter change on the same path does not create a second view.
4. Attach the error handler
Use addVueError as your application's global error handler, then mount:
import { createApp } from 'vue';
import { addVueError } from '@atatus/browser-rum-vue';
import App from './App.vue';
const app = createApp(App);
app.config.errorHandler = addVueError;
app.use(router);
app.mount('#app');
Reference: Vue Router and Error Tracking.
Next.js
The Next.js package supports both the App Router and the Pages Router. It always tracks views itself, so you do not set a router option.
1. Install the package
Install it alongside the base agent:
$ npm install @atatus/browser-rum @atatus/browser-rum-nextjs
2. Create an instrumentation client file
Create instrumentation-client.js in the root of your project. Next.js loads it before your application code runs:
import { atatusRum } from '@atatus/browser-rum';
import { nextjsPlugin, onRouterTransitionStart } from '@atatus/browser-rum-nextjs';
export { onRouterTransitionStart };
atatusRum.init({
licenseKey: '<license-key>',
appName: 'Storefront',
env: 'production',
sessionSampleRate: 100,
sessionReplaySampleRate: 30,
trackUserInteractions: true,
plugins: [nextjsPlugin()],
});
Re-exporting onRouterTransitionStart is required for the App Router. Next.js calls it when a navigation starts, which is how the agent captures the destination URL before React renders. The Pages Router does not need it, so you can drop that line in a Pages Router application.
3. Add the router component
For the App Router, render AtatusAppRouter in your root layout:
// app/layout.tsx
import { AtatusAppRouter } from '@atatus/browser-rum-nextjs';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<AtatusAppRouter />
{children}
</body>
</html>
);
}
For the Pages Router, render AtatusPagesRouter in your custom App:
// pages/_app.tsx
import type { AppProps } from 'next/app';
import { AtatusPagesRouter } from '@atatus/browser-rum-nextjs';
export default function MyApp({ Component, pageProps }: AppProps) {
return (
<>
<AtatusPagesRouter />
<Component {...pageProps} />
</>
);
}
Both components normalize dynamic segments, so concrete URLs aggregate into one view:
| Actual URL | View name |
|---|---|
/about |
/about |
/users/123 |
/users/[id] |
/users/123/posts/456 |
/users/[userId]/posts/[postId] |
/docs/a/b/c |
/docs/[...slug] |
4. Report errors
The App Router catches uncaught exceptions in error.tsx files. Report from there with addNextjsError:
// app/error.tsx
'use client'
import { useEffect } from 'react';
import { addNextjsError } from '@atatus/browser-rum-nextjs';
export default function Error({ error, reset }: { error: Error & { digest?: string }; reset: () => void }) {
useEffect(() => {
addNextjsError(error);
}, [error]);
return <button onClick={reset}>Try again</button>;
}
For errors in the root layout itself, add the same reporting to app/global-error.tsx. That file replaces the root layout, so it has to render its own <html> and <body> tags.
A Server Component error reaches the browser as a generic message with an error.digest hash attached. Use that hash to match the RUM error to the full stack trace in your server logs. Client Component errors arrive with their original message and no digest.
For the Pages Router, wrap your app in ErrorBoundary instead:
// pages/_app.tsx
import { AtatusPagesRouter, ErrorBoundary } from '@atatus/browser-rum-nextjs';
export default function MyApp({ Component, pageProps }: AppProps) {
return (
<>
<AtatusPagesRouter />
<ErrorBoundary fallback={({ resetError }) => (
<div>
<p>Something went wrong</p>
<button onClick={resetError}>Try again</button>
</div>
)}>
<Component {...pageProps} />
</ErrorBoundary>
</>
);
}
Reference: Next.js instrumentation-client, Next.js error handling, and Error Tracking.
Nuxt
The Nuxt package tracks file-based routes and reports errors from both Vue's error handler and Nuxt's app:error hook.
1. Install the package
Install it alongside the base agent:
$ npm install @atatus/browser-rum @atatus/browser-rum-nuxt
2. Create a client-side plugin
Create plugins/atatus-rum.client.ts and initialize the agent there, passing the router and the Nuxt app:
import { atatusRum } from '@atatus/browser-rum';
import { nuxtRumPlugin } from '@atatus/browser-rum-nuxt';
import { defineNuxtPlugin, useNuxtApp, useRouter } from 'nuxt/app';
export default defineNuxtPlugin({
name: 'atatus-rum',
enforce: 'pre',
setup() {
atatusRum.init({
licenseKey: '<license-key>',
appName: 'Storefront',
env: 'production',
sessionSampleRate: 100,
sessionReplaySampleRate: 30,
trackUserInteractions: true,
plugins: [
nuxtRumPlugin({
router: useRouter(),
nuxtApp: useNuxtApp(),
}),
],
});
},
});
The .client.ts suffix keeps the plugin out of the server bundle, and enforce: 'pre' runs it before your other plugins so it can catch startup errors.
Passing nuxtApp is optional but worth doing. With it, the integration reports Vue component errors and Nuxt startup errors automatically, so step 3 is only needed for errors you catch yourself.
3. Report errors you handle yourself
Use addNuxtError anywhere you catch an error you still want recorded:
import { addNuxtError } from '@atatus/browser-rum-nuxt';
try {
await loadDashboard();
} catch (error) {
addNuxtError(error);
}
Reference: Nuxt plugins and Error Tracking.
Any other framework
There is no package for Svelte, Solid, Ember, or a hand-rolled router, so name views yourself from your router's navigation hook. Use setViewName, which renames the view the agent already created:
router.afterEach((to) => {
if (window.ATATUS_RUM) {
window.ATATUS_RUM.setViewName(to.name);
}
});
Use setViewName rather than startView here. The agent already creates a view on each navigation, so calling startView as well produces two views for every route change. setViewName renames the existing one.
If you would rather control views completely, turn off automatic tracking and create each one yourself:
atatusRum.init({
licenseKey: '<license-key>',
appName: 'Storefront',
trackViewsManually: true,
});
router.afterEach((to) => {
atatusRum.startView(to.name);
});
Next steps
- Configuration: every initialization option.
- Error Tracking: source maps and manual error reporting.
- Session Replay: recording and privacy options.
- API Reference: methods you can call after
init().
+1-415-800-4104