# TracerKit SDK — @tracerkit/js + @tracerkit/react

Typed wrappers over the TracerKit data layer
(`window.tracerkitLayer`) and loader API — integrate with autocomplete
instead of copy-pasted snippets. Zero runtime dependencies; every call is
SSR-safe and no-ops when the loader isn't present.

The types mirror the data layer spec field-for-field:
https://tracerkit.com/docs/data-layer.md

## Install

```sh
npm install @tracerkit/js        # framework-agnostic core
npm install @tracerkit/react     # React/Next component + hook (includes the core)
```

> Publishing to npm is a manual ops step — the packages are ready-to-publish from packages/sdk-js and packages/sdk-react in the TracerKit repo. Until they land on the registry, install from a checkout (npm install <path>) or keep using the plain snippets.

## @tracerkit/js

| Function | Returns | Notes |
| --- | --- | --- |
| `pushEvent(name, opts?)` | `void` | Pushes { event: name, ...opts } onto window.tracerkitLayer. name autocompletes the five canonical events (page_view, view_item, add_to_cart, begin_checkout, purchase) and accepts any custom-trigger name. opts: { value?, currency?, items?, page_location?, page_title? } — extra fields pass through (the spec ignores unknown fields). |
| `pushPurchase({ order_id, value, currency, items, email?, phone? })` | `void` | The purchase event, typed field-for-field from the data layer spec. Push exactly once per order — TracerKit dedupes on order_id. email/phone are optional enhanced-matching fields, hashed in-browser by the loader. |
| `identify({ email?, phone? })` | `void` | Enhanced matching. Values are hashed in-browser (SHA-256) by the loader — raw values never leave the page. Works before the loader boots (queued on the layer). |
| `consent(state)` | `void` | Same contract as tracerkit.consent(): TracerKit categories ({ analytics, marketing }) or GCMv2 signal names ({ ad_storage, analytics_storage, ad_user_data, ad_personalization }), booleans or "granted"/"denied". Loader present → forwarded to tracerkit.consent() (blocked tags replay). Before the loader boots → normalized onto window.__tkConsent, the preset the loader applies on boot. |
| `lastEventId()` | `string | undefined` | The loader's per-fire event id. Pass it to a browser pixel (fbq eventID / ttq event_id) so the platform dedupes pixel + server into one conversion. undefined until the loader has fired an event. |

```ts
import {
  pushEvent,
  pushPurchase,
  identify,
  consent,
} from "@tracerkit/js";

// funnel events — typed to the data layer spec
pushEvent("add_to_cart", {
  value: 79.98,
  currency: "USD",
  items: [{ id: "SKU-123", name: "Canvas Tote", price: 39.99, quantity: 2 }],
});

// exactly once per order, on the confirmation page
pushPurchase({
  order_id: "1001",
  value: 129.97,
  currency: "USD",
  items: [
    { id: "SKU-123", name: "Canvas Tote", price: 39.99, quantity: 2 },
    { id: "SKU-456", name: "Wool Beanie", price: 49.99, quantity: 1 },
  ],
  email: "jane@example.com", // optional — hashed in-browser
});

// enhanced matching outside of purchase (e.g. after login)
identify({ email: "jane@example.com" });

// consent — categories or GCMv2 signal names, before or after the loader
consent({ analytics: true, marketing: false });
consent({ ad_storage: "granted", ad_user_data: "granted" });
```

### Pixel dedup

```ts
import { lastEventId, pushPurchase } from "@tracerkit/js";

pushPurchase(order);
// if you also run a browser pixel, share the event id so the platform
// dedupes pixel + server into ONE conversion:
fbq("track", "Purchase", { value, currency }, { eventID: lastEventId() });
```

### Loader-optional semantics

The SDK never requires the loader to be booted first:

- During SSR (no `window`) every call is a silent no-op.
- Layer pushes made before the loader boots queue on the plain
  `window.tracerkitLayer` array and are drained on boot.
- `consent()` before boot stages normalized state on
  `window.__tkConsent` — the site preset the loader applies first on
  boot. Normalization uses the exact same helper as the loader
  (`normalizeConsent`), so category expansion (`marketing` ⇔
  `ad_storage` + `ad_user_data` + `ad_personalization`) and
  last-write-wins ordering behave identically before and after boot.

## @tracerkit/react

| Function | Returns | Notes |
| --- | --- | --- |
| `<TracerKit siteKey="tk_..." api? preview? />` | `JSX (a plain async <script> tag)` | Renders the loader snippet — no next/script, works in React 18+ and in Next.js App Router server components. api sets a verified custom-domain origin (script src + data-api); preview sets data-preview for draft configs. |
| `useTracerKit()` | `{ pushEvent, pushPurchase, identify, consent, lastEventId }` | The typed @tracerkit/js API as a stable object for event handlers and effects. All methods SSR-safe. The same functions are also re-exported from @tracerkit/react directly. |

### Next.js App Router

Place `<TracerKit/>` inside `<head>` in your root layout — it renders
a plain async `<script>` (no `next/script`), which is exactly how the
install docs recommend embedding the loader:

```tsx
// app/layout.tsx (Next.js App Router)
import { TracerKit } from "@tracerkit/react";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <head>
        {/* the loader ships in the initial HTML — the plain-script
            equivalent of next/script's beforeInteractive placement */}
        <TracerKit siteKey="tk_YOUR_SITE_KEY" />
      </head>
      <body>{children}</body>
    </html>
  );
}
```

Then push events from anywhere:

```tsx
"use client";
import { useTracerKit } from "@tracerkit/react";

export function CheckoutButton({ cart }: { cart: Cart }) {
  const { pushEvent } = useTracerKit();
  return (
    <button
      onClick={() =>
        pushEvent("begin_checkout", {
          value: cart.total,
          currency: cart.currency,
          items: cart.items,
        })
      }
    >
      Checkout
    </button>
  );
}
```

Custom domain + preview mode map to the snippet attributes:

```tsx
<TracerKit
  siteKey="tk_YOUR_SITE_KEY"
  api="https://metrics.example.com"  // verified custom domain → src + data-api
  preview="PREVIEW_TOKEN"            // data-preview → draft config
/>
```

## Related docs

- Install guide (snippet, dedupe rules, verification): https://tracerkit.com/docs/install.md
- Data layer spec (the contract these types mirror): https://tracerkit.com/docs/data-layer.md

Machine-readable version of this page: https://tracerkit.com/docs/sdk.md
HTML version: https://tracerkit.com/docs/sdk
