React
React has a dedicated wrapper: @vskstudio/takt-react (provider, hooks, widgets, web component), built on the @vskstudio/takt-core core.
pnpm add @vskstudio/takt-react @vskstudio/takt-core
# or: npm install @vskstudio/takt-react @vskstudio/takt-core
# or: yarn add @vskstudio/takt-react @vskstudio/takt-core
# or: bun add @vskstudio/takt-react @vskstudio/takt-core react (^18 || ^19) and @vskstudio/takt-core (>=0.8.1) are peer dependencies: the core is not installed automatically, add it explicitly.
The package is fully typed and SSR / RSC-safe (init only happens on mount, in the browser). The . entry ships the 'use client' directive, so it drops straight into the Next.js App Router. It exposes two entry points:
@vskstudio/takt-react— the<Takt>provider, theuseTakt()anduseTaktEvent()hooks, the<TaktEvent>component, the<TaktBadge>and<TaktEmbed>widgets, plus the core helpers it re-exports (badgeUrl(),embedUrl(),createStats(),PublicApiError).@vskstudio/takt-react/element— the React-free<takt-analytics>web component for plain HTML.
<Takt> provider + useTakt()
Place <Takt> once near the root: it initializes the instance on mount, emits the initial pageview and wires up SPA navigation. useTakt() retrieves the shared instance in any descendant component to emit events.
// app/layout.tsx (or your root component)
import { Takt } from '@vskstudio/takt-react'
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<Takt domain="example.com" outbound files>
{children}
</Takt>
</body>
</html>
)
} <Takt> only renders a provider around its children: under the Next.js App Router, rendering the <html> and <body> tags remains the root layout’s job.
import { useTakt } from '@vskstudio/takt-react'
function SignupButton() {
const takt = useTakt()
return (
<>
<button onClick={() => takt.track('Signup', { props: { plan: 'pro' } })}>
Sign up
</button>
<button
onClick={() =>
takt.track('Purchase', {
props: { plan: 'pro' },
revenue: { amount: '29', currency: 'EUR' }
})
}
>
Buy
</button>
</>
)
} <Takt> provider props:
| Prop | Type | Default | Role |
|---|---|---|---|
domain | string | location.hostname | Site identifier |
endpoint | string | https://taktlytics.com/api/event | Ingestion URL. Pass /api/event for a same-origin first-party proxy. |
scriptOrigin | string | — | First-party origin to derive the endpoint from (that origin followed by /api/event). endpoint wins over it. |
outbound | boolean | false | Tracks outbound links |
files | boolean \| string[] | false | Tracks downloads (optional extension list) |
spa | boolean | true | Tracks client navigation (auto pageviews) |
track404 | boolean | false | Reports a 404 event on error pages ([data-takt-404] / <meta name="takt:404"> marker, or a 404 HTTP status). |
tagged | boolean | false | Auto-tracks clicks on [data-takt-event] elements |
respectDnt | boolean | true | Respects Do Not Track |
excludeLocalhost | boolean | true | Ignores localhost / private IPs |
enabled | boolean | true | Master switch — false disables all tracking |
sampleRate | number | 1 | Fraction of sessions tracked (0–1) |
trackQuery | boolean | false | Keeps the query string in URLs |
queryParams | string[] | — | Parameters kept when trackQuery is false (allowlist) |
exclude | string[] | — | Path prefixes never tracked. Segment-bounded: /app covers /app and /app/… but not /application. |
scrubUrl | (url: string) => string | — | Transforms every URL before it is sent |
Config props are read once when <Takt> mounts — changing them afterwards has no effect; remount the component to reconfigure.
enabled, sampleRate, trackQuery, queryParams, exclude, scrubUrl) are available as props, with no separate core instance. Only debug is not exposed: to enable it, instantiate the core yourself with createTakt() — and do not use <Takt> at the same time, or you will double your pageviews. Never call init() at module level in a file rendered on the server: the core touches history as it is instantiated.scrubUrl is a function: it cannot cross the Next.js server → client boundary. Render <Takt> from a client component if you pass it.
useTakt() never throws: called outside a <Takt> or during SSR, it returns a no-op instance. Calls stay harmless, but the first track() / pageview() emits a console.warn reporting that <Takt> is not mounted.
useTaktEvent() hook + <TaktEvent>
For declarative click tracking, useTaktEvent() returns an { onClick } you spread onto any element. The instance is resolved at click time (no stale closure), with a fallback to the core’s track():
import { useTaktEvent } from '@vskstudio/takt-react'
function BuyButtons() {
const signup = useTaktEvent({ name: 'Signup', props: { plan: 'pro' } })
const purchase = useTaktEvent({
name: 'Purchase',
revenue: { amount: '29', currency: 'EUR' }
})
return (
<>
<button {...signup}>Sign up</button>
<button {...purchase}>Buy</button>
</>
)
} <TaktEvent> wraps a single child and composes its existing onClick, so you can annotate an element without touching its handler. It forwards refs to the child:
import { TaktEvent } from '@vskstudio/takt-react'
function Cta({ onClick }) {
return (
<TaktEvent name="Signup" props={{ plan: 'pro' }}>
<button onClick={onClick}>Sign up</button>
</TaktEvent>
)
} The child’s original onClick still fires; tracking runs alongside it.
<TaktBadge> and <TaktEmbed> widgets
To display a site’s public stats (see Widgets), the package ships two drop-in components:
import { TaktBadge, TaktEmbed } from '@vskstudio/takt-react'
function Footer() {
return (
<>
<TaktBadge domain="example.com" variant="b" glyph="dash" lang="en" />
<TaktEmbed domain="example.com" theme="dark" lang="en" />
</>
)
} <TaktBadge> renders an <img> (loading="lazy", decoding="async"): variant is a, b or d, glyph is unplug, dash, off or eyeoff. <TaktEmbed> renders a 404×264 <iframe> by default, loading="lazy" and sandboxed: theme is light, dark or auto. Both accept lang (fr / en), a host prop to point at another Takt origin, and forward their remaining attributes to the rendered element.
<takt-analytics> web component
For plain HTML or a non-React framework, import the React-free (auto-registered) element:
<script type="module">
import '@vskstudio/takt-react/element'
</script>
<takt-analytics domain="example.com" outbound files></takt-analytics> Attributes mirror the props, in kebab-case:
- Flags (presence = on, value ignored):
outbound,files,tagged. - On by default, set
="false"(or="0") to turn them off:spa,respect-dnt,exclude-localhost. - Values:
domain,endpoint,script-origin,enabled,sample-rate,track-query, plusquery-paramsandexcludewhich take a comma-separated list.
<takt-analytics domain="example.com" spa="false" respect-dnt="false"></takt-analytics> files takes no extension list here, and scrubUrl has no equivalent: those are values only the <Takt> provider receives as props. 404 detection likewise goes through the track404 prop. The bundle is SSR-safe: importing it on the server is a no-op as long as customElements does not exist.