Skip to content

@dumbledor/sdk

Typed analytics for React and Next.js using your project website ID.

@dumbledor/sdk is the npm package for React, Next.js, and server runtimes. Use the CDN script tag for static sites — one line, no build step.

Install

bash
pnpm add @dumbledor/sdk
# or: npm install @dumbledor/sdk

Website ID

Copy your website ID from project settings under Tracking. This is the same UUID used in the CDN script tag's data-website-id attribute.

Browser

Initialize once, then call track, page, or identify anywhere in your app.

typescript
import { init, track, identify } from "@dumbledor/sdk";

init({
  websiteId: "YOUR_PROJECT_ID",
  honorDoNotTrack: true,
});

track("signup", { plan: "pro" });
identify("user_123", { plan: "pro" });

Initialization

One global client backs getClient(), track / page / identify, hooks, and DumbledorProvider. The provider calls init() internally — you do not need a separate init() when using the provider.

FieldType
init() + track / page / identifypattern
init() + hookspattern
DumbledorProviderpattern
createClient()pattern

One config

Do not mix init() and DumbledorProvider with different options. The last initialization wins.

Identify

identify(userId, traits?) links a stable user ID to the current visitor session. Dumbledor does not set cookies. Ingest derives the session from your project ID, IP address, user agent, and a daily salt.

  • Browser: call identify after login in the same session where you track events.
  • Retroactive: events earlier in that session also show the linked user ID once identify runs.
  • Daily boundary: the session salt rotates at UTC midnight — call identify again for returning users on a new day.
  • Traits: accepted in the payload but not stored yet.
  • Server: pass the user’s ip and userAgent in context so ingest resolves the same session as their browser.
typescript
// Browser — after login
identify("user_123", { plan: "pro" });
track("purchase", { amount: 99 });

// Server — forward request headers to match the browser session
await dumbledor.identify("user_123", { plan: "pro" }, {
  ip: request.headers.get("x-forwarded-for") ?? undefined,
  userAgent: request.headers.get("user-agent") ?? undefined,
});

React

DumbledorProvider calls init() internally. Wrap your app, add PageView for automatic pageviews on mount and route changes.

tsx
import { DumbledorProvider, PageView, useTrack } from "@dumbledor/sdk/react";

export function App() {
  return (
    <DumbledorProvider websiteId="YOUR_PROJECT_ID" honorDoNotTrack>
      <PageView />
      <CheckoutButton />
    </DumbledorProvider>
  );
}

function CheckoutButton() {
  const track = useTrack();
  return <button onClick={() => track("checkout_clicked")}>Checkout</button>;
}
FieldType
DumbledorProvidercomponent
PageViewcomponent
useTrack()hook
useIdentify()hook
usePage()hook

Next.js App Router

Import from @dumbledor/sdk/next.AppRouterPageView listens to usePathname() and useSearchParams() so client navigations are tracked without calling page() yourself.

tsx
import { Suspense } from "react";
import { DumbledorProvider, AppRouterPageView } from "@dumbledor/sdk/next";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <DumbledorProvider websiteId={process.env.NEXT_PUBLIC_DUMBLEDOR_WEBSITE_ID!}>
          <Suspense fallback={null}>
            <AppRouterPageView />
          </Suspense>
          {children}
        </DumbledorProvider>
      </body>
    </html>
  );
}

Pageviews and SSR

Pageviews are sent from the client after hydration, not during server renders. That keeps counts accurate and avoids double-counting prefetches or bot traffic.

Server

Use @dumbledor/sdk/server in route handlers, webhooks, or background jobs for events that never touch the browser.

typescript
import { createServerClient } from "@dumbledor/sdk/server";

const dumbledor = createServerClient({
  websiteId: process.env.DUMBLEDOR_WEBSITE_ID!,
});

await dumbledor.track(
  "subscription_created",
  { plan: "pro" },
  {
    url: "https://app.example.com/billing",
    userAgent: request.headers.get("user-agent") ?? undefined,
    ip: request.headers.get("x-forwarded-for") ?? undefined,
  },
);

Server pageviews

Do not use the server client to count SSR page renders. Send pageviews from the browser SDK instead.

Configuration

FieldType
websiteIduuid
ingestUrlstring
honorDoNotTrackboolean
disabledboolean
typescript
init({
  websiteId: "YOUR_PROJECT_ID",
  ingestUrl: "http://localhost:3001",
  disabled: process.env.NODE_ENV === "development",
});

Package exports

FieldType
@dumbledor/sdkentry
@dumbledor/sdk/reactentry
@dumbledor/sdk/nextentry
@dumbledor/sdk/serverentry

Open the dashboard after wiring the SDK to confirm events are flowing.