Callra
Guides

Framework adapters

Copy-paste adapter examples for Next.js, React Router, Remix, and Nuxt so Callra keeps SPA route metadata accurate.

Callra's shared script.js snippet works everywhere, but for SPAs with framework routing the adapter packages are the best install path.

They do three things for you:

  • mount the shared tracker once
  • keep route_template aligned with your framework's dynamic route pattern
  • send SPA pageviews without requiring manual window.va('route', ...) calls

Use the raw snippet only when you cannot mount a framework adapter.

When to use an adapter

Use an adapter if your app has client-side routing and you care about readable route reports such as:

  • /docs/[slug]
  • /dashboard/[siteId]
  • /blog/[...segments]

Without route metadata, Callra can still track page URLs, but route-based Web Analytics and Speed Insights breakdowns may collapse into exact paths or (unknown route).

Shared requirements

Install the package for your framework and pass:

  • siteId: the Callra site id
  • host: your Callra app URL, such as https://analytics.example.com

All examples below assume this site id:

const siteId = 'site_123';
const host = 'https://analytics.example.com';

Next.js

For the App Router, mount the adapter once in a client component near the root.

'use client';

import { Analytics } from '@callra/analytics-next';

export function CallraAnalytics() {
  return <Analytics siteId="site_123" host="https://analytics.example.com" />;
}

Then render that component from app/layout.tsx or from a shared providers wrapper.

import { CallraAnalytics } from './callra-analytics';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <body>
        {children}
        <CallraAnalytics />
      </body>
    </html>
  );
}

React Router

Mount the adapter once inside your app shell.

import { Analytics } from '@callra/analytics-react-router';

export function App() {
  return (
    <>
      <Analytics siteId="site_123" host="https://analytics.example.com" />
      {/* your router UI */}
    </>
  );
}

Remix

Mount the adapter once in the main app document.

import { Analytics } from '@callra/analytics-remix';
import { Outlet } from '@remix-run/react';

export default function App() {
  return (
    <html>
      <body>
        <Analytics siteId="site_123" host="https://analytics.example.com" />
        <Outlet />
      </body>
    </html>
  );
}

Nuxt

Register the adapter in a plugin so it initializes once.

import { defineNuxtPlugin } from '#app';
import { injectNuxtAnalytics } from '@callra/analytics-nuxt';

export default defineNuxtPlugin(() => {
  injectNuxtAnalytics({
    siteId: 'site_123',
    host: 'https://analytics.example.com'
  });
});

Optional runtime settings

All adapters ultimately drive the same Callra runtime, so you can still pass normal runtime options such as environment labels.

<Analytics
  siteId="site_123"
  host="https://analytics.example.com"
  environment="staging"
/>

You can also keep using custom events:

window.va('event', 'Signup', {
  plan: 'pro',
  location: 'pricing-page'
});

If you cannot use an adapter

Stay on the raw snippet and send route metadata yourself whenever the framework route changes.

window.va('route', { route: '/docs/[slug]' });

That fallback works, but adapters are safer because they keep route state and pageview timing aligned automatically.