@klickbee-agency/tracking
v0.6.2
Published
Framework-agnostic analytics client for [klickbee-tracking](https://github.com/), a PostHog/Hotjar-style product analytics tool. Ships ESM + CJS with types, plus an optional React/Next.js integration.
Readme
@klickbee/tracking
Framework-agnostic analytics client for klickbee-tracking, a PostHog/Hotjar-style product analytics tool. Ships ESM + CJS with types, plus an optional React/Next.js integration.
- Pageviews, custom events, identify, autocapture clicks
- Optional session replay (rrweb) tied to the analytics session
- Buffered + batched delivery (timer + size based),
sendBeaconon page hide - SSR-safe: every method is a no-op on the server
- Persists an
anonymousId(localStorage) and per-tabsessionId(sessionStorage)
Install
This package is published to a self-hosted GitLab Package Registry under the
@klickbee scope. Point the scope at the registry in your project (or user)
.npmrc, then install as usual. Replace <GITLAB_HOST> and <GITLAB_PROJECT_ID>
with your values, and provide an auth token via GITLAB_NPM_TOKEN:
# .npmrc (in the consuming project)
@klickbee:registry=https://<GITLAB_HOST>/api/v4/projects/<GITLAB_PROJECT_ID>/packages/npm/
//<GITLAB_HOST>/api/v4/projects/<GITLAB_PROJECT_ID>/packages/npm/:_authToken=${GITLAB_NPM_TOKEN}export GITLAB_NPM_TOKEN=<personal-or-deploy-token-with-read_package_registry>
pnpm add @klickbee/tracking
# React integration also needs react / react-dom (peer deps)Core usage
import { createClient } from "@klickbee/tracking";
const tracking = createClient({
projectKey: "pk_live_xxx",
apiHost: "http://localhost:3020", // events POST to `${apiHost}/api/ingest`
autocapture: true, // optional, capture clicks
flushAt: 20, // optional, flush after N events (default 20)
flushInterval: 5000, // optional, flush every N ms (default 5000)
debug: false,
});
tracking.pageview();
tracking.track("signup_clicked", { plan: "pro" });
tracking.identify("user_123", { email: "[email protected]" });
tracking.reset(); // on logout: clears identity + new anonymousId
tracking.flush(); // force-send buffered eventsConfig
| Option | Type | Default | Description |
| --------------- | --------- | -------- | ---------------------------------------------------- |
| projectKey | string | N/A | Public ingest key (pk_...). |
| apiHost | string | N/A | klickbee-tracking origin. |
| autocapture | boolean | false | Capture document clicks as autocapture events. |
| replay | boolean | false | Record a session replay (rrweb) tied to this session.|
| flushAt | number | 20 | Flush once the buffer reaches this many events. |
| flushInterval | number | 5000 | Flush cadence in milliseconds. |
| debug | boolean | false | Log internal activity to the console. |
| requireConsent | boolean | false | Capture/send nothing until grantConsent() is called. |
| respectDoNotTrack | boolean | true | Honour the browser Do-Not-Track signal (full no-op). |
Privacy / GDPR controls
Consent gating
With requireConsent: true the client captures nothing, starts no timers, runs no
replay and makes no network calls until you explicitly grant consent. The decision
is persisted in localStorage (kb_consent) and auto-resumed on the next load.
const tracking = createClient({
projectKey: "pk_...",
apiHost: "https://analytics.example.com",
requireConsent: true,
});
// After the visitor accepts your cookie/consent banner:
tracking.grantConsent(); // starts timers, scroll, autocapture, replay (if configured)
// If they later withdraw consent:
tracking.revokeConsent(); // stops replay + tracking, clears the buffer, stops timersDo-Not-Track
respectDoNotTrack defaults to true. When the browser signals DNT
(navigator.doNotTrack === "1", window.doNotTrack, or navigator.msDoNotTrack),
the client becomes a full no-op regardless of consent: no timers, no autocapture,
no replay, no network. Set respectDoNotTrack: false to ignore the signal.
Replay PII masking
Session replay masks personal data by default:
All typed input values are masked (
maskAllInputs: true), with password, email and tel inputs always masked.You can opt elements out of the recording with these selectors:
| Marker | Effect | | ------------------------------- | ------------------------------------------------- | |
data-kb-block/.kb-block| Block the element entirely (replaced by a box). | |data-kb-mask/.kb-mask| Mask the element's text content. | |data-kb-ignore/.kb-ignore| Ignore the element (its changes aren't recorded). |<input class="kb-block" name="ssn" /> <div data-kb-mask>Account balance: $12,345</div> <div data-kb-ignore>noisy live widget</div>
These defaults are overridable when constructing a ReplayRecorder directly via
maskAllInputs, blockSelector and maskTextSelector, but masking always takes
precedence over the fidelity options (inlineStylesheet/images/fonts).
Session replay
Set replay: true to record the page with rrweb.
The recorder reuses the client's sessionId/anonymousId, buffers the rrweb event
stream, and posts bounded chunks to ${apiHost}/api/ingest/replay (flushing on a
timer, when the buffer fills, and on page hide via sendBeacon). Recording starts
automatically; you can also control it manually:
const tracking = createClient({ projectKey: "pk_...", apiHost: "...", replay: true });
tracking.startReplay(); // idempotent
tracking.stopReplay();With the React provider, pass replay: true in config; the provider handles it.
Next.js App Router
The SDK never imports next/navigation; the app passes the current pathname so
the provider can auto-track pageviews on route changes.
// app/providers.tsx
"use client";
import { TrackingProvider } from "@klickbee/tracking/react";
import { usePathname } from "next/navigation";
import type { ReactNode } from "react";
export function Analytics({ children }: { children: ReactNode }) {
const pathname = usePathname();
return (
<TrackingProvider
config={{
projectKey: "pk_...",
apiHost: "http://localhost:3020",
autocapture: true,
}}
pathname={pathname}
>
{children}
</TrackingProvider>
);
}// app/layout.tsx
import { Analytics } from "./providers";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<Analytics>{children}</Analytics>
</body>
</html>
);
}Then in any client component:
"use client";
import { useTracking } from "@klickbee/tracking/react";
export function SignupButton() {
const tracking = useTracking();
return <button onClick={() => tracking.track("signup_clicked")}>Sign up</button>;
}Manual pageview wiring
If you prefer to handle pageviews yourself, omit pathname and use the hook:
import { useAutoPageview, useTracking } from "@klickbee/tracking/react";
import { usePathname } from "next/navigation";
const tracking = useTracking();
useAutoPageview(tracking, usePathname());License
MIT
