takt

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, the useTakt() and useTaktEvent() 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:

PropTypeDefaultRole
domainstringlocation.hostnameSite identifier
endpointstringhttps://taktlytics.com/api/eventIngestion URL. Pass /api/event for a same-origin first-party proxy.
scriptOriginstringFirst-party origin to derive the endpoint from (that origin followed by /api/event). endpoint wins over it.
outboundbooleanfalseTracks outbound links
filesboolean \| string[]falseTracks downloads (optional extension list)
spabooleantrueTracks client navigation (auto pageviews)
track404booleanfalseReports a 404 event on error pages ([data-takt-404] / <meta name="takt:404"> marker, or a 404 HTTP status).
taggedbooleanfalseAuto-tracks clicks on [data-takt-event] elements
respectDntbooleantrueRespects Do Not Track
excludeLocalhostbooleantrueIgnores localhost / private IPs
enabledbooleantrueMaster switch — false disables all tracking
sampleRatenumber1Fraction of sessions tracked (0–1)
trackQuerybooleanfalseKeeps the query string in URLs
queryParamsstring[]Parameters kept when trackQuery is false (allowlist)
excludestring[]Path prefixes never tracked. Segment-bounded: /app covers /app and /app/… but not /application.
scrubUrl(url: string) => stringTransforms 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.

The provider forwards all of this configuration to the core: the advanced settings (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, plus query-params and exclude which 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.