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() + consent
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.
@vskstudio/takt-core.