takt

Vanilla JS

@vskstudio/takt-core is the framework-agnostic core every wrapper builds on. Fully typed, zero dependencies, it works in any bundler — and ships the CDN snippet too.

The import is server-safe: the module touches no browser API at load time, and track() / pageview() / optOut() / optIn() are no-ops until init() has run. init() itself belongs in the browser though: it wires history and reads location.

pnpm add @vskstudio/takt-core
# or: npm install @vskstudio/takt-core
# or: yarn add @vskstudio/takt-core
# or: bun add @vskstudio/takt-core

init() + track()

Call init() once at startup: it creates the shared instance, emits the initial pageview and wires up SPA navigation (pushState/replaceState/popstate/hashchange). Those last two are driven by the auto option: with auto: false, neither the history patch nor the boot pageview is installed. Then emit events from anywhere with track().

import { init, track } from '@vskstudio/takt-core'

// Once, at app startup.
init({ domain: 'example.com', outbound: true })

// Everywhere else.
track('Signup', { props: { plan: 'pro' } })

init() is idempotent: a second call disposes the previous instance’s listeners before creating a new one. The full option list lives in the configuration and the API reference.

Revenue

track() accepts a revenue object alongside props — the amount is a string matching \d+(\.\d{1,2})?, the currency a 3-letter code (sent upper-cased):

import { track } from '@vskstudio/takt-core'

track('Purchase', {
  props: { plan: 'pro' },
  revenue: { amount: '29.00', currency: 'EUR' }
})

If the amount or the currency fails validation, the revenue is dropped (with a console warning) but the event is still sent.

The instance returned by init() — or by createTakt(), which builds a standalone instance with no singleton — exposes the exact same signature, handy when you’d rather not rely on the singleton:

const takt = init({ domain: 'example.com' })

takt.track('Purchase', { revenue: { amount: '29.00', currency: 'EUR' } })

pageview() emits a pageview manually — rarely needed while auto is on, since navigation is then tracked for you; it is the right tool once you turn auto off (which is what the Astro integration does). optOut() / optIn() drive per-visitor consent, persisted in localStorage under the takt_ignore key:

import { pageview, optOut, optIn } from '@vskstudio/takt-core'

optOut() // no event sent
optIn()  // resumes tracking
pageview()

See Privacy for the exact order of the guardrails.

CDN snippet

No bundler? The same package ships a ready-to-use script — a single tag in the <head>, init done from data-* attributes:

<script defer src="https://cdn.jsdelivr.net/npm/@vskstudio/takt-core/dist/takt.js" data-domain="example.com"></script>

With neither data-endpoint nor data-script-origin, the snippet sends to https://taktlytics.com/api/event — same as the npm SDK. Pass data-endpoint="/api/event" for a same-origin first-party proxy.

On the snippet side, only window.takt(...) (the track function) is exposed — see Installation for the queue shim and API reference for the global.

Source on GitHub: github.com/vskstudio/takt-core · package on npm: @vskstudio/takt-core.