React
React a un wrapper dédié : @vskstudio/takt-react (provider, hooks, widgets, web component), bâti sur le cœur @vskstudio/takt-core.
pnpm add @vskstudio/takt-react @vskstudio/takt-core
# ou : npm install @vskstudio/takt-react @vskstudio/takt-core
# ou : yarn add @vskstudio/takt-react @vskstudio/takt-core
# ou : bun add @vskstudio/takt-react @vskstudio/takt-core react (^18 || ^19) et @vskstudio/takt-core (>=0.10.0 depuis la 0.8.0) sont des peer dependencies : le cœur n’est pas installé automatiquement, ajoute-le explicitement.
Le package est entièrement typé et sûr au SSR / RSC (l’init n’a lieu qu’au montage, côté navigateur). L’entrée . embarque la directive 'use client', donc il s’intègre directement à l’App Router de Next.js. Il expose deux points d’entrée :
@vskstudio/takt-react: le provider<Takt>, les hooksuseTakt()etuseTaktEvent(), le composant<TaktEvent>, les widgets<TaktBadge>et<TaktEmbed>, ainsi que les helpers du cœur réexportés (badgeUrl(),embedUrl(),createStats(),PublicApiError,optOut(),optIn(),isOptedOut()) et le helper de routeurreactRouterTemplate().@vskstudio/takt-react/element: le web component<takt-analytics>sans React, pour du HTML pur.
Provider <Takt> + useTakt()
Place <Takt> une fois près de la racine : il initialise l’instance au montage, émet le pageview initial et câble la navigation SPA. useTakt() récupère l’instance partagée dans n’importe quel composant descendant pour émettre des events.
// app/layout.tsx (ou ton composant racine)
import { Takt } from '@vskstudio/takt-react'
export default function RootLayout({ children }) {
return (
<html lang="fr">
<body>
<Takt domain="exemple.fr" outbound files>
{children}
</Takt>
</body>
</html>
)
} <Takt> ne rend qu’un provider autour de ses enfants : sous l’App Router de Next.js, c’est bien au layout racine de rendre les balises <html> et <body>.
import { useTakt } from '@vskstudio/takt-react'
function SignupButton() {
const takt = useTakt()
return (
<>
<button onClick={() => takt.track('Signup', { props: { plan: 'pro' } })}>
S'inscrire
</button>
<button
onClick={() =>
takt.track('Purchase', {
props: { plan: 'pro' },
revenue: { amount: '29', currency: 'EUR' }
})
}
>
Acheter
</button>
</>
)
} Props du provider <Takt> :
| Prop | Type | Défaut | Rôle |
|---|---|---|---|
domain | string | location.hostname | Identifiant du site |
endpoint | string | https://taktlytics.com/api/event | URL d’ingestion. Passe /api/event pour un proxy first-party same-origin. |
scriptOrigin | string | aucun | Origine first-party dont dériver l’endpoint (l’origine suivie de /api/event). endpoint l’emporte. |
outbound | boolean | false | Suit les liens sortants |
files | boolean \| string[] | false | Suit les téléchargements (liste d’extensions optionnelle) |
spa | boolean | true | Suit la navigation client (pageviews auto) |
track404 | boolean | false | Signale un événement 404 sur les pages d’erreur (marqueur [data-takt-404] / <meta name="takt:404">, ou statut HTTP 404). |
tagged | boolean | false | Suit automatiquement les clics sur les éléments [data-takt-event] |
respectDnt | boolean | true | Respecte Do Not Track |
excludeLocalhost | boolean | true | Ignore localhost / IP privées |
enabled | boolean | true | Interrupteur général : false coupe toute mesure |
sampleRate | number | 1 | Fraction de sessions échantillonnées (0 à 1) |
trackQuery | boolean | false | Conserve la query string dans les URL |
queryParams | string[] | aucun | Paramètres conservés quand trackQuery est false (liste blanche) |
exclude | string[] | aucun | Préfixes de chemin jamais suivis. Bornés au segment : /app couvre /app et /app/… mais pas /application. |
scrubUrl | (url: string) => string | aucun | Transforme chaque URL avant envoi (page, referrer, url des liens sortants et des téléchargements, path des 404) |
debug | boolean | false | Trace chaque payload dans la console avant l’envoi |
redactRoutes | string[] | aucun | Motifs de routes sensibles : un chemin qui correspond part sous la forme du motif, ex. ['/verify/[token]', '/reset/:code'] (voir Masquage de routes) |
redactUndecodablePaths | boolean | true | Non exposée par ce wrapper : se règle sur le SDK core (init() / createTakt()), voir Chemins illisibles |
routeTemplates | boolean | false | Envoie chaque page sous la forme de son gabarit de route ; exige routeTemplate |
routeTemplate | () => string \| null | aucun | Renvoie le gabarit de la route courante, ex. () => reactRouterTemplate(router) ; lu quand routeTemplates est actif. Prop du composant uniquement |
Les props de config sont lues une seule fois au montage de <Takt>. Les changer ensuite n’a aucun effet, remonte donc le composant pour reconfigurer. Seule exception : <Takt> appelle toujours la fonction routeTemplate du dernier rendu.
enabled, sampleRate, trackQuery, queryParams, exclude, scrubUrl) sont disponibles en props, sans piloter le cœur à côté, tout comme debug depuis la 0.7.0. N'appelle jamais init() au niveau module dans un fichier rendu côté serveur : le cœur touche history à l'instanciation.scrubUrl est une fonction : elle ne peut pas traverser la frontière serveur → client de Next.js. Rends <Takt> depuis un composant client si tu la passes. Même chose pour routeTemplate.
useTakt() ne lève jamais : appelé hors d’un <Takt> ou pendant le SSR, il renvoie une instance no-op. Les appels restent inoffensifs, mais le premier track() / pageview() émet un console.warn signalant que <Takt> n’est pas monté.
Le consentement, lui, marche avant le montage : useTakt().optOut(), useTakt().optIn() et useTakt().isOptedOut() agissent directement sur le choix stocké, tout comme les fonctions optOut, optIn et isOptedOut exportées par le package. Une bannière de consentement peut donc s’afficher en premier, et l’instance créée ensuite respecte le choix.
import { useState } from 'react'
import { isOptedOut, optIn, optOut } from '@vskstudio/takt-react'
function AnalyticsToggle() {
const [blocked, setBlocked] = useState(isOptedOut)
const toggle = () => {
if (blocked) optIn()
else optOut()
setBlocked(isOptedOut())
}
return <button onClick={toggle}>{blocked ? 'Activer la mesure' : 'Désactiver la mesure'}</button>
} Masquage de routes
La query string est retirée par défaut, mais le chemin part en clair : /verify/abc123 transmet le jeton. Liste les routes sensibles dans redactRoutes : un chemin qui correspond part sous la forme du motif, toutes les autres pages gardent leur chemin réel. Les motifs acceptent [param], [[optionnel]], [...reste], (groupe), :param, :param?, * et **. La règle couvre l’URL de la page, le referrer du même site, les liens sortants et les téléchargements, et le chemin des 404.
<Takt domain="exemple.fr" redactRoutes={['/verify/[token]', '/reset/:code', '/invoices/:id']}>
{children}
</Takt> C’est le réglage recommandé pour Next.js : l’App Router n’expose aucun gabarit de route côté client. Liste les routes dynamiques qui portent un secret, avec les mêmes noms [param] que tes dossiers app/.
routeTemplates envoie chaque page sous la forme de son gabarit : /users/42 devient /users/:id. Il convient aux applications entièrement privées ; sur un site public, il fusionne tous les articles en une seule ligne. Takt doit connaître la route trouvée, qu’il lit via routeTemplate. Avec un data router React Router (createBrowserRouter), passe reactRouterTemplate(router) :
import { RouterProvider, createBrowserRouter } from 'react-router'
import { Takt, reactRouterTemplate } from '@vskstudio/takt-react'
const router = createBrowserRouter([
{
path: '/',
element: <Layout />,
children: [
{ index: true, element: <Home /> },
{ path: 'users/:id', element: <User /> },
{ path: 'docs/*', element: <Docs /> }
]
}
])
export function App() {
return (
<Takt domain="exemple.fr" routeTemplates routeTemplate={() => reactRouterTemplate(router)}>
<RouterProvider router={router} />
</Takt>
)
} reactRouterTemplate joint le path de chaque route trouvée (/users/:id, /docs/*), ignore les routes index et les layouts sans path, et renvoie null quand rien ne correspond. Dans ce cas, redactRoutes s’applique quand même, sinon le chemin réel part. Il ne lit que router.state.matches et n’ajoute donc aucune dépendance à react-router. Détails dans Vie privée.
Hook useTaktEvent() + <TaktEvent>
Pour du tracking de clic déclaratif, useTaktEvent() renvoie un { onClick } que tu étales sur n’importe quel élément. L’instance est résolue au moment du clic (pas de closure périmée), avec un repli sur le track() du cœur :
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}>S'inscrire</button>
<button {...purchase}>Acheter</button>
</>
)
} <TaktEvent> enveloppe un unique enfant et compose son onClick existant, pour annoter un élément sans toucher à son handler. Il forwarde les refs vers l’enfant :
import { TaktEvent } from '@vskstudio/takt-react'
function Cta({ onClick }) {
return (
<TaktEvent name="Signup" props={{ plan: 'pro' }}>
<button onClick={onClick}>S'inscrire</button>
</TaktEvent>
)
} Le onClick d’origine de l’enfant se déclenche toujours ; le tracking tourne en parallèle.
Widgets <TaktBadge> et <TaktEmbed>
Pour afficher les statistiques publiques d’un site (voir Widgets), le package fournit deux composants prêts à poser :
import { TaktBadge, TaktEmbed } from '@vskstudio/takt-react'
function Footer() {
return (
<>
<TaktBadge domain="exemple.fr" variant="b" glyph="dash" lang="fr" />
<TaktEmbed domain="exemple.fr" theme="dark" lang="fr" />
</>
)
} <TaktBadge> rend une <img> (loading="lazy", decoding="async") : variant vaut a, b ou d, glyph vaut unplug, dash, off ou eyeoff. <TaktEmbed> rend une <iframe> de 404×264 par défaut, loading="lazy" et sandboxée : theme vaut light, dark ou auto. Les deux acceptent lang (fr / en), une prop host pour pointer une autre origine Takt, et forwardent le reste de leurs attributs à l’élément rendu.
Web component <takt-analytics>
Pour du HTML pur ou un framework non-React, importe l’élément sans React (auto-enregistré) :
<script type="module">
import '@vskstudio/takt-react/element'
</script>
<takt-analytics domain="exemple.fr" outbound files></takt-analytics> Les attributs miroir des props, en kebab-case :
- Drapeaux (présence = actif, valeur ignorée) :
outbound,files,tagged. - Actifs par défaut, mets
="false"(ou="0") pour les couper :spa,respect-dnt,exclude-localhost. - Valeurs :
domain,endpoint,script-origin,enabled,sample-rate,track-query,debug(lu seulement si l’attribut est présent), ainsi quequery-params,excludeetredact-routesqui prennent une liste séparée par des virgules (redact-routes="/verify/[token],/reset/:code").
<takt-analytics domain="exemple.fr" spa="false" respect-dnt="false"></takt-analytics> routeTemplates et routeTemplate n’ont pas d’attribut : l’élément n’a pas de routeur. files ne prend pas de liste d’extensions ici, et scrubUrl n’a pas d’équivalent : ce sont des valeurs que seul le provider <Takt> reçoit en props. La détection 404 passe elle aussi par la prop track404. Le bundle est sûr au SSR : l’importer côté serveur est un no-op tant que customElements n’existe pas.