Muro
Installation guides

Install Muro on Next.js

Add the Muro script to a Next.js app with the App Router or the Pages Router. Client-side navigation is tracked automatically.

In Next.js, the Muro script goes in your root layout (App Router) or your custom document (Pages Router), so it loads on every route. Client-side navigation with <Link> and router.push() is tracked without any extra code.

Steps

Copy your snippet

In Muro, go to Settings → Project → Tracking script and click Copy script. You need the project ID from it, the value of data-project-id.

Add the script to your root layout

Use a plain <script async> tag inside <head>.

Open app/layout.tsx (or app/layout.js) and add the script to the <head> of your root layout:

// app/layout.tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <head>
        <script
          async
          src="https://api.muroanalytics.com/muro.js"
          data-project-id="YOUR_PROJECT_ID"
        />
      </head>
      <body>{children}</body>
    </html>
  );
}

If your layout has no <head> element yet, add one as shown.

Open pages/_document.tsx (or pages/_document.js). If the file doesn't exist, create it. Add the script inside <Head>:

// pages/_document.tsx
import { Html, Head, Main, NextScript } from 'next/document';

export default function Document() {
  return (
    <Html lang="en">
      <Head>
        <script
          async
          src="https://api.muroanalytics.com/muro.js"
          data-project-id="YOUR_PROJECT_ID"
        />
      </Head>
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  );
}

Use Head from next/document here, not next/head.

Why not next/script?

Don't use next/script with strategy="afterInteractive" for Muro. That strategy waits until React has hydrated, which on a JavaScript-heavy page can be several seconds after load. Visitors who leave before then are never counted, so your visitor and bounce numbers come out wrong. A plain <script async> in <head> starts downloading straight away without blocking rendering, the same way Google Analytics, Plausible and Fathom load.

Deploy

Commit and deploy as usual, for example by pushing to the branch Vercel deploys from.

Verify it works

  1. Open your deployed site.
  2. Open DevTools, go to the Network tab, filter by collect and reload. You should see a POST to cdn.muroanalytics.com/collect with status 202.
  3. Click a <Link> to another page. A second collect request appears for the new route.
  4. Or, in Muro, set the time picker to Live. Your visit appears within about 30 seconds.

Good to know

  • No router code needed. Muro records a pageview each time Next.js calls history.pushState, and on back and forward. Don't add a useEffect on route changes that calls window.muro.track, or every page is counted twice.
  • No double count on load. Next.js calls history.replaceState during hydration. Muro ignores replaceState on purpose, so the first page is counted once. See Single-page apps.
  • Content Security Policy. If you set a CSP in next.config.js, middleware or vercel.json, allow https://api.muroanalytics.com in script-src and https://cdn.muroanalytics.com in connect-src. See Fix Content Security Policy errors.
  • Keep previews and localhost out. Muro counts every page that loads the script, including next dev and preview deployments. See Keep staging and localhost out of your data.
  • Calling window.muro in TypeScript. Use window.muro?.track(...) from Client Components only, and add a type declaration once. See Track custom events.

Next steps

On this page