Integrations

Next.js

Databuddy works with both the App Router and Pages Router. Add it once near the root of your app; page views and client-side route changes are tracked automatically.

Start with pageviews and one custom event, then use the same data for goals and funnels. The Free plan includes 10,000 events per month; error tracking starts on Hobby. See current plans and limits.

Try a runnable App Router example

Download the minimal Next.js example to test a root layout, client-side navigation, and one plan_selected event. Extract it outside this monorepo, run bun install --frozen-lockfile, and follow its README to configure a separate test website. Without a Client ID, the example keeps collection disabled and explains the missing setting.

The example does not create accounts or take payments. For confirmed backend outcomes, continue with the signup-to-paid implementation guide. For recorded test results and their limitations, inspect the route-event and delivery-recovery check.

Before you start

  1. Create a free account and add your website in Databuddy.
  2. Copy that website's Client ID from its tracking setup. This is a public identifier; keep server API keys out of browser code.
  3. Add the Client ID to .env.local at your Next.js project root, then restart your dev server:
NEXT_PUBLIC_DATABUDDY_CLIENT_ID=your-client-id

Install

bun add @databuddy/sdk

You can also install with npm or yarn:

npm install @databuddy/sdk
yarn add @databuddy/sdk

App Router

Add <Databuddy /> to app/layout.tsx:

import { Databuddy } from "@databuddy/sdk/react";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <Databuddy
          clientId={process.env.NEXT_PUBLIC_DATABUDDY_CLIENT_ID!}
        />
        {children}
      </body>
    </html>
  );
}

The SDK's React entry point includes the client boundary, so your root layout can stay a Server Component. Custom event handlers belong in a Client Component, as shown below.

Pages Router

Add <Databuddy /> to pages/_app.tsx:

import { Databuddy } from "@databuddy/sdk/react";
import type { AppProps } from "next/app";

export default function MyApp({ Component, pageProps }: AppProps) {
  return (
    <>
      <Databuddy
        clientId={process.env.NEXT_PUBLIC_DATABUDDY_CLIENT_ID!}
      />
      <Component {...pageProps} />
    </>
  );
}

Databuddy sends the first page view when it loads and listens for route changes. Do not add your own router.events or usePathname screen-view tracking unless you intentionally disabled Databuddy's automatic route tracking.

Script Tag Setup

If you prefer not to use the React component, add the script in pages/_document.tsx:

import { Head, Html, Main, NextScript } from "next/document";

export default function Document() {
  return (
    <Html lang="en">
      <Head>
        <script
          async
          crossOrigin="anonymous"
          data-client-id={process.env.NEXT_PUBLIC_DATABUDDY_CLIENT_ID}
          src="https://cdn.databuddy.cc/databuddy.js"
        />
      </Head>
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  );
}

Use either the React component or the script tag, not both.

Optional errors and Web Vitals

Add trackWebVitals to the React component to collect performance measurements. Add trackErrors if your plan includes error tracking. Both are off by default, and the collected measurements count toward your event allowance.

<Databuddy
  clientId={process.env.NEXT_PUBLIC_DATABUDDY_CLIENT_ID!}
  trackWebVitals
  trackErrors
/>

For the script tag, the equivalent attributes are data-track-web-vitals and data-track-errors.

Track Custom Events

Use the SDK helper from client components:

"use client";

import { track } from "@databuddy/sdk";

export function SubscribeButton() {
  return (
    <button
      onClick={() =>
        track("subscribe_clicked", {
          location: "header",
        })
      }
      type="button"
    >
      Subscribe
    </button>
  );
}

This records a click, not a completed subscription. Record outcomes such as account creation or payment after the backend confirms success, using the Node SDK. Send each completed outcome from one place so a browser call and server call do not double-count it.

Use stable event names and small properties such as the button location. Do not send emails, passwords, tokens, or entire form values.

Verify your first events

Verify on your deployed site. For a local test, the standard tracker skips localhost: temporarily add debug to <Databuddy /> to load the debug bundle, or use https://cdn.databuddy.cc/databuddy-debug.js in your script tag. Use a separate Databuddy website and Client ID for local test data, and remove debug mode before deploying.

  1. Open your site and use a client-side link to visit a second page.
  2. In the browser Network tab, confirm the Databuddy script loads from cdn.databuddy.cc and requests reach basket.databuddy.cc. Events are batched, so allow a few seconds for delivery.
  3. Open that website in Databuddy and check for your two pageviews. Check the same date range and website as your test.
  4. Trigger the custom event once and confirm its name appears on the Events page. A successful pageview does not prove your custom event is instrumented.

Keep the delivery stages separate: a loaded script proves installation, an outgoing request proves an attempted send, and a successful collector response must be checked for rejection details. Confirm the event in the intended website's report before treating the journey as working. The browser track() helper returns void; updating your UI after calling it is not a delivery acknowledgement.

Use Back and Forward after the first soft navigation and check the paths again. For a recovery check, block the script in browser developer tools, reload, and confirm your app still works. Unblock it, reload, and repeat the journey. This verifies recovery on a new page load; it does not prove durable retry of events attempted while collection was blocked.

Databuddy DevTools can help inspect the tracker and custom events while you develop.

Production Setup

Add NEXT_PUBLIC_DATABUDDY_CLIENT_ID to your deployment provider before building. On Vercel, choose the deployment environments that should collect data. Next.js embeds public environment variables at build time, so rebuild and redeploy after changing the Client ID.

To avoid test data, disable tracking outside production:

<Databuddy
  clientId={process.env.NEXT_PUBLIC_DATABUDDY_CLIENT_ID!}
  disabled={process.env.NODE_ENV !== "production"}
/>

NODE_ENV is also production for many preview deployments. If previews should stay out of production analytics, use your deployment provider's environment indicator for disabled, or give previews a separate Databuddy website and Client ID.

Serve Databuddy from your own domain

Ad blockers and privacy extensions often block requests to cdn.databuddy.cc and basket.databuddy.cc, so visitors who use them never reach your analytics. Two rewrites let your own domain serve the script and receive events, which keeps those visitors in your data.

Add the rewrites to next.config.ts:

import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  async rewrites() {
    return [
      { source: "/_p/s.js", destination: "https://cdn.databuddy.cc/databuddy.js" },
      { source: "/_p/e/:path*", destination: "https://basket.databuddy.cc/:path*" },
    ];
  },
};

export default nextConfig;

Then point <Databuddy /> at those paths:

<Databuddy
  clientId={process.env.NEXT_PUBLIC_DATABUDDY_CLIENT_ID!}
  scriptUrl="/_p/s.js"
  apiUrl="/_p/e"
/>

If you load Databuddy through the c15t consent manager, pass the same scriptUrl and apiUrl values to its databuddy() helper instead.

Keep these points in mind when you set it up:

  • Proxy both the script and the event endpoint. Blockers block both hosts, so a proxied script alone still loses its events.
  • Choose a path prefix that doesn't describe analytics. Blocklists match paths such as /analytics, /stats, and /track, so a neutral prefix like /_p keeps working longer.
  • Visitor locations stay accurate. The rewrite passes each visitor's IP address to Databuddy in the X-Forwarded-For header, and Databuddy uses it for location. If you add your own proxy in front of Next.js, make sure it forwards that header.
  • With anonymizeVisitorIds: "auto", proxied events always get anonymized visitor IDs. Auto mode only trusts the country of an IP that reaches Databuddy directly, so it treats a forwarded IP as unknown and takes the privacy-safe choice.
  • Keep usePixel off, because pixel mode needs an absolute apiUrl. Feature flags still load from api.databuddy.cc, since these rewrites cover tracking only.

To check the setup, open your deployed site with the Network tab open. The script should load from /_p/s.js and events should post to paths such as /_p/e/batch, with no script or event requests going to cdn.databuddy.cc or basket.databuddy.cc. Feature flag requests to api.databuddy.cc are expected. The tracker sends nothing from localhost, so test on a deployment rather than your dev server.

Replace an existing tracker

  1. Install Databuddy once and verify pageviews and one important event before removing the old tracker.
  2. Write down what each existing conversion means. Keep a button click separate from a completed signup, and send the completed event only after success.
  3. Recreate the goals and funnels you need in Databuddy. Confirm that their paths and event names match the events you just verified.
  4. Remove the old provider's script and manual route listeners when you are ready to switch. Remove any duplicate Databuddy script added through a tag manager or another layout.

If you briefly run both providers, compare the same dates and definitions. Different session, consent, and bot-filtering rules can produce different totals; an exact count match is not a setup requirement.

Troubleshooting

No data in the dashboard

  1. Confirm NEXT_PUBLIC_DATABUDDY_CLIENT_ID is set and matches the website in Databuddy.
  2. Check the browser Network tab for requests to basket.databuddy.cc.
  3. Make sure the component or script is included only once.
  4. If disabled is set, confirm it is false in the environment you are testing.
  5. Check your site's Content Security Policy and blockers. Allow scripts from https://cdn.databuddy.cc and connections to https://basket.databuddy.cc. If visitors' ad blockers stop those requests, serve Databuddy from your own domain.
  6. For local tests, use the debug bundle as described above. For deployment tests, rebuild after setting the public Client ID.

Pageviews work, but an outcome is missing

Confirm the event call runs after the actual outcome, uses the same website, and is not skipped by an error or redirect. If you send it from a serverless route handler, flush the Node SDK before the handler exits. Check the custom event helpers and server event setup.

Pageviews appear twice

Check for a second script in a tag manager, nested layout, or shared provider. If automatic route tracking is enabled, remove any manual pageview effect before repeating a direct load, soft navigation, and Back/Forward check. Keep custom events tied to user actions or confirmed outcomes rather than a component render.

If you are evaluating libraries before installation, read the React and Next.js SDK comparison. If the analytics service must run on infrastructure you manage, review the self-hosted platform comparison separately from the client-side integration.

How is this guide?