How to Integrate Plausible Analytics in a Next.js App (Without Getting Blocked)

How to Integrate Plausible Analytics in a Next.js App (Without Getting Blocked)

Plausible Analytics is a lightweight, privacy-focused, cookie-free analytics solution. In this article, we’ll implement it inside a Next.js App Router project in a way that bypasses ad blockers using proxying. By proxying the tracking script and API requests through your own domain, you significantly reduce the chance of them being blocked by common ad-blocking extensions.

We’ll also ensure that page views are correctly tracked on every route change in a client-side rendered app.

This guide assumes you are self-hosting Plausible on a custom subdomain such as analytics.yourdomain.com.

Proxy the Plausible Script and API

To reduce the chance of being blocked by ad blockers, we'll proxy Plausible's script and API through your own domain.

In your next.config.js, add the following:

js
Copy code
async rewrites() {
  return [
    {
      source: "/js/script.js",
      destination:
        "https://analytics.yourdomain.com/js/script.file-downloads.hash.outbound-links.pageview-props.revenue.tagged-events.js",
    },
    {
      source: "/api/event",
      destination: "https://analytics.yourdomain.com/api/event",
    },
  ];
}

Next.js uses this to route requests internally without triggering a redirect (i.e., the user sees the original URL in their browser).

  • When a user or script on your site requests /js/script.js or /api/event, the request is internally proxied to the external URL.
  • The browser still thinks it loaded locally, but the content is coming from analytics.yourdomain.com.

What is this script variant?

The URL points to a self-hosted and enhanced version of the Plausible tracking script:

script.file-downloads.hash.outbound-links.pageview-props.revenue.tagged-events.js

This version includes support for:

  • File downloads
  • URL hash tracking
  • Outbound link clicks
  • Custom pageview properties
  • Revenue tracking
  • Tagged events

This is ideal if you want richer insights without modifying your app further.

Create a Route Tracker Component

Create a new file: app/RouteTracker.tsx

tsx
Copy code
'use client';

import { usePathname } from 'next/navigation';
import { useEffect } from 'react';

const RouteTracker = () => {
  const pathname = usePathname();

  useEffect(() => {
    // 1. Add the Plausible script to the DOM if not already added
    if (!document.getElementById("next-p")) {
      const script = document.createElement("script");
      script.id = "next-p";
      script.async = true;
      script.defer = true;
      script.setAttribute("data-domain", "yourdomain.com");
      script.src = "/js/script.js"; // Note: this uses the proxied script
      document.head.appendChild(script);
    }

    // 2. Add Plausible's minimal global initializer
    if (!document.getElementById("next-p-init")) {
      const initScript = document.createElement("script");
      initScript.id = "next-p-init";
      initScript.innerHTML =
        "window.plausible = window.plausible || function() { (window.plausible.q = window.plausible.q || []).push(arguments) }";
      document.head.appendChild(initScript);
    }

    // 3. Manually track a pageview when route changes
    const trackPageview = (url: string) => {
      const eventData = {
        name: "pageview",
        url,
        domain: window.location.hostname,
        ...(document.referrer && { referrer: document.referrer }),
      };

      fetch("/api/event", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify(eventData),
      }).catch((err) => console.error("Error tracking pageview:", err));
    };

    trackPageview(pathname);
  }, [pathname]);

  return null;
};

export default RouteTracker;

What this does

  • Dynamically injects the Plausible script into the page, ensuring it's only loaded once.
  • Adds a global Plausible initializer, so custom events can be tracked if needed.
  • Manually sends a pageview event whenever the route changes in the client, which is essential in App Router / SPA apps.

Use the Component in Your Layout

Open your root layout file (app/layout.tsx) and import the tracker:

tsx
Copy code
import RouteTracker from "@/src/hooks/plausible"; // adjust path to match your project

Then include it in your layout:

tsx
Copy code
<body>
  <RouteTracker />
  {children}
</body>

Final Notes

  • This implementation ensures Plausible is loaded only on the client and is less likely to be blocked.
  • All route changes are tracked, making it perfect for SPA-like behavior with App Router.
  • You benefit from all of Plausible’s extended features using the custom script variant.

By combining script proxying and client-side tracking, you get powerful, privacy-compliant analytics without sacrificing usability or insight.

Table of Contents