Lightweight, privacy-first analytics SDK for tgram-analytics.
- Zero dependencies — only browser APIs
- < 2 KB gzipped — won't slow your page down
- TypeScript-first — full type definitions included, no
@typespackage needed - Privacy-friendly — no cookies, no fingerprinting, respects Do Not Track
- SPA-ready — auto-tracks route changes with React Router, Vue Router, Next.js, etc.
Before using this SDK you need:
- A running tgram-analytics server. See the server repo for setup instructions.
- A project API key. Create one by sending
/add myapp.comto the Telegram bot. The bot replies with a key that starts withproj_.
npm install tgram-analyticsHost dist/index.iife.js from your server (or a CDN) and load it on your page. It exposes the global TGA object.
<script src="https://your-server.com/sdk/tga.min.js"></script>
<script>
TGA.init("proj_xxx", { serverUrl: "https://your-server.com" });
</script>import TGA from "tgram-analytics";
// Step 1 — initialise once, at the top of your app.
TGA.init("proj_abc123", {
serverUrl: "https://analytics.example.com",
});
// Step 2 — track custom events anywhere in your code.
TGA.track("purchase", { amount: 49, currency: "USD", plan: "pro" });Pageviews are sent automatically on load and on every SPA route change — no extra code needed.
const { default: TGA } = require("tgram-analytics");
TGA.init("proj_abc123", { serverUrl: "https://analytics.example.com" });
TGA.track("signup");Initialises the SDK. Call this once, before using any other method.
TGA.init("proj_abc123", {
serverUrl: "https://analytics.example.com",
autoPageview: true,
respectDNT: true,
});Throws an Error if apiKey is invalid or serverUrl is missing.
Tracks a custom named event.
TGA.track("purchase", { amount: 49, currency: "USD", plan: "pro" });
TGA.track("signup"); // properties are optional| Parameter | Type | Description |
|---|---|---|
eventName |
string |
Event identifier, e.g. "purchase", "signup". |
properties |
EventProperties? |
Optional key-value metadata. Values must be string, number, boolean, or null. |
Tracks a pageview. You rarely need this — autoPageview: true (the default) handles it automatically.
// Use defaults (current URL and document.referrer).
TGA.pageview();
// Pass explicit values.
TGA.pageview("/checkout", "https://example.com/cart");| Parameter | Type | Default |
|---|---|---|
url |
string? |
window.location.pathname + search |
referrer |
string? |
document.referrer |
Attaches persistent properties to every subsequent track() call.
// After a user logs in:
TGA.identify({ plan: "pro", locale: "en-US" });
// Every subsequent track() call now includes plan and locale automatically.
TGA.track("purchase", { amount: 49 }); // => properties: { plan, locale, amount }Per-event properties (passed directly to track()) override identify() properties when keys conflict.
Manually opts the current user in or out of tracking.
// Disable all tracking (e.g. user declined consent banner).
TGA.opt("out");
// Re-enable tracking.
TGA.opt("in");Note: The opt-out state is not persisted across page loads. If you want persistence, store the preference yourself (e.g. in
localStorage) and callTGA.opt("out")on every page load when needed.
Force-sends all buffered events immediately. Only relevant when batch is enabled.
// Ensure no events are lost before a programmatic navigation.
await TGA.flush();
router.push("/thank-you");Returns Promise<void>.
Starts a new session. Clears the session ID from sessionStorage and generates a fresh one. Also clears all properties set via identify().
function onLogout() {
TGA.reset();
// Future events are now attributed to a new anonymous session.
}All options are passed as the second argument to TGA.init().
| Option | Type | Default | Description |
|---|---|---|---|
serverUrl |
string |
— | Required. Base URL of your tgram-analytics server. No trailing slash. |
autoPageview |
boolean |
true |
Automatically track pageviews on load and SPA navigations. |
respectDNT |
boolean |
true |
Honour the browser's Do Not Track setting. |
batch |
boolean | BatchOptions |
false |
Buffer events before sending. See Batching. |
sessionId |
string |
— | Override the auto-generated session ID. Rarely needed. |
| Option | Type | Default | Description |
|---|---|---|---|
maxSize |
number |
10 |
Send when the buffer reaches this many events. |
maxWait |
number |
5000 |
Send after this many milliseconds, even if maxSize is not reached. |
When autoPageview is true (the default), the SDK automatically sends a pageview after every distinct URL change — no configuration required. It works with:
- React Router —
pushState/popstate - Vue Router —
pushState/replaceState - Next.js App Router —
pushState - Svelte Kit —
pushState - Any other framework that uses the History API
Consecutive navigations to the same URL are deduplicated, so rapid replaceState calls (e.g. syncing query params) do not generate duplicate pageviews.
Batching is useful when you track many events in a short time (e.g. scroll depth, click heatmaps). Instead of one request per event, the SDK buffers events and sends them together.
TGA.init("proj_xxx", {
serverUrl: "https://analytics.example.com",
batch: { maxSize: 20, maxWait: 3000 },
});The queue flushes automatically when:
- The buffer reaches
maxSizeevents. maxWaitmilliseconds have passed since the first event in the batch.- The user navigates away from the page (
visibilitychange,pagehide). - You call
TGA.flush()manually.
- No cookies. Session IDs are stored in
sessionStorageonly, which is scoped to a single tab and cleared automatically when the tab closes. - No fingerprinting. The SDK does not access canvas, WebGL, audio, or any other fingerprinting surface.
- Do Not Track. When
respectDNT: true(the default), the SDK checksnavigator.doNotTrackon init. If DNT is enabled, all tracking is silently disabled — no requests are sent. - Write-only API. The server's ingestion endpoints are write-only. No user data can be read back via the API key.
The server enforces a domain allowlist per project. When the allowlist is not empty, requests from origins not on the list are rejected with 403 Forbidden.
To add your domain:
- Open the Telegram bot.
- Send
/projectsand select your project. - Tap ⚙️ Settings → Domain allowlist and add your domain.
If you are developing locally, either leave the allowlist empty (allows all origins) or add http://localhost:3000 (or whichever port you use).
The server must also have CORS middleware enabled. If you deployed using the official server image, CORS is configured automatically.
Full type definitions are included in the package — no separate @types package is needed.
import TGA from "tgram-analytics";
import type { TGAOptions, EventProperties } from "tgram-analytics";
const options: TGAOptions = {
serverUrl: "https://analytics.example.com",
autoPageview: true,
};
TGA.init("proj_xxx", options);
const props: EventProperties = { amount: 49, plan: "pro" };
TGA.track("purchase", props);Contributions are welcome! Please open an issue or pull request on GitHub.
git clone https://github.com/tg-analytics/sdk-js
cd sdk-js
npm install| Command | Description |
|---|---|
npm run build |
Build all output formats to dist/ |
npm test |
Run the test suite |
npm run typecheck |
Check TypeScript types (no emit) |
npm run check |
Lint + format with Biome (auto-fixes) |
npm run lint |
Lint only |
npm run format |
Format only |
- Run
npm run check— fixes linting and formatting automatically. - Run
npm test— all tests must pass. - Run
npm run typecheck— zero TypeScript errors. - Run
npm run build— the build must succeed.
MIT — see LICENSE.