Astro
Astro a une intégration dédiée : @vskstudio/takt-astro, bâtie sur le cœur @vskstudio/takt-core. Elle injecte un petit runtime navigateur qui boote Takt, émet le pageview initial et suit la navigation client — y compris les View Transitions d'Astro.
pnpm add @vskstudio/takt-astro @vskstudio/takt-core @vskstudio/takt-core (>=0.10.0 depuis la 0.8.0) et astro (>=4) sont des peer dependencies. Le runtime est sûr au SSR / prerender : il est gardé par un test typeof window et ne s’exécute que dans le navigateur.
Cette page est la référence des options. Pour l’intégration de bout en bout et son piège principal — l’écouteur astro:page-load ajouté en trop, qui double chaque page vue — voir le guide analytics sans cookie sur un site Astro.
window.__takt : la seconde voie chargée ne réinitialise rien, elle ne fait strictement rien. Tu n'aurais donc pas de double comptage, juste un second script embarqué pour rien.Intégration (recommandé)
Ajoute takt() à la liste integrations de ta config Astro :
// astro.config.mjs
import { defineConfig } from 'astro/config'
import takt from '@vskstudio/takt-astro'
export default defineConfig({
integrations: [takt({ domain: 'exemple.fr' })]
}) Composant <Takt />
Pour un contrôle par layout, place le composant dans le <head> d’un layout :
---
import Takt from '@vskstudio/takt-astro/Takt.astro'
---
<head>
<Takt domain="exemple.fr" />
</head> L’intégration et le composant acceptent les mêmes options, à une exception près : scrubUrl est une fonction et n’est acceptée que par l’intégration (voir la note sous le tableau).
| Option | Type | Défaut | Rôle |
|---|---|---|---|
domain | string | location.hostname | Identifiant du site |
endpoint | string | https://taktlytics.com/api/event | URL d’ingestion ; mets /api/event pour un proxy first-party same-origin |
scriptOrigin | string | — | Origine dont dériver l’endpoint ({origin}/api/event), pour un domaine custom proxifié ; endpoint reste prioritaire |
outbound | boolean | false | Suit les liens sortants |
files | boolean | false | Suit les téléchargements |
spa | boolean | true | Suit la navigation client (mappé sur l’option auto du cœur) |
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 les éléments porteurs de 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, plus aucun event |
sampleRate | number | 1 | Fraction de visiteurs suivis, de 0 à 1 |
trackQuery | boolean | false | Envoie l’URL complète au lieu d’en retirer query et hash |
queryParams | string[] | — | Liste blanche de paramètres conservés quand trackQuery est off |
exclude | string[] | — | Préfixes de chemin jamais suivis, ex. ['/app','/compte'] (bornés au segment : /app couvre /app et /app/…, pas /application) |
scrubUrl | (url: string) => string | — | Intégration seulement. Réécrit 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 du navigateur avant l’envoi |
redactRoutes | string[] | aucun | Motifs de routes envoyés à la place du chemin réel, ex. ['/verify/[token]'] envoie /verify/[token] au lieu de /verify/abc123 (voir Masquage de routes) |
routeTemplates | boolean | false | Envoie chaque page sous la forme de son gabarit de route Astro (/blog/[slug]), lu depuis Astro.routePattern. Exige Astro 5 et la balise de route |
scrubUrl est une fonction : elle est sérialisée à la compilation via toString() puis réévaluée dans le navigateur. Elle doit donc être autonome — aucune variable de closure ni référence au scope englobant — et ne jamais être construite à partir d'entrées utilisateur. Le composant <Takt />, lui, sérialise sa config en îlot de données JSON, qui ne peut pas transporter de fonction : lui passer scrubUrl lève une erreur à la compilation plutôt que de l'ignorer en silence.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 : chaque chemin qui correspond part sous la forme de son motif, les autres pages gardent leur chemin réel.
takt({ redactRoutes: ['/verify/[token]', '/invoices/[id].pdf'] }) 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. Les motifs suivent la syntaxe d’Astro ([param], [...reste]) et acceptent aussi :param et *.
Pour envoyer plutôt chaque page sous la forme de son gabarit de route (/blog/bonjour devient /blog/[slug]), active routeTemplates. Astro connaît le gabarit côté serveur (Astro.routePattern) : il voyage dans une balise <meta name="takt:route"> que le runtime relit à chaque pageview, y compris après une navigation ClientRouter. Avec l’intégration, rends cette balise avec <TaktRoute /> dans le <head> de ton layout :
// astro.config.mjs
export default defineConfig({
integrations: [takt({ routeTemplates: true })]
}) ---
import TaktRoute from '@vskstudio/takt-astro/TaktRoute.astro'
---
<head>
<TaktRoute />
</head> Le composant <Takt /> rend la balise lui-même quand routeTemplates est actif :
<Takt domain="exemple.fr" routeTemplates /> Astro.routePattern existe depuis Astro 5. Sous Astro 4, aucune balise n’est rendue : redactRoutes s’applique quand même, sinon le chemin réel part. Ce mode fusionne tous les articles d’un blog public en une seule ligne, il convient donc mieux aux espaces privés ; sur un site public, redactRoutes est en général le bon outil. Détails dans Vie privée.
View Transitions
Le ClientRouter d’Astro effectue plusieurs opérations d’historique par navigation (un replaceState de restauration de scroll, puis un pushState). Le patch d’historique du cœur compterait donc en double. L’intégration l’écarte : elle initialise le cœur avec auto: false, ce qui désactive à la fois le patch pushState/replaceState et le pageview de boot.
Le runtime pilote donc les pageviews depuis le cycle de vie d’Astro :
- un pageview initial explicite au boot du script — ce qui couvre aussi les sites MPA classiques, où le script est réexécuté à chaque chargement de page et où
astro:after-swapne se déclenche jamais ; - puis un pageview par événement
astro:after-swap, émis exactement une fois par navigation duClientRouter(échanges de DOM des View Transitions et retour/avant compris), quandspaest actif.
Chaque navigation est ainsi comptée exactement une fois.
astro:page-load pour suivre les pages : le runtime enregistre déjà son propre astro:after-swap. Le tien en installerait un second et compterait chaque navigation deux fois.history.pushState que tu pilotes à la main — filtres, onglets, pagination hors ClientRouter — ne déclenche aucun astro:after-swap, donc aucun pageview. C'est le seul cas où appeler pageview() toi-même est la bonne réponse.Events personnalisés
Les fonctions du cœur sont réexportées par commodité :
import { track } from '@vskstudio/takt-astro'
track('Signup', { revenue: { amount: '9.00', currency: 'USD' } }) track/pageview/optOut/optIn/isOptedOut s’exécutent côté navigateur : appelle-les depuis un <script> client ou un composant d’un framework UI hydraté.
Consentement
optOut, optIn et isOptedOut n’attendent pas le démarrage du runtime : ils lisent et écrivent directement le choix stocké. Une bannière de consentement peut donc s’exécuter avant le runtime injecté, et l’instance qu’il démarre ensuite respecte le choix. Appelle-les depuis un <script> client, puisque le choix vit dans le navigateur.
<button id="analytics-toggle"></button>
<script>
import { isOptedOut, optIn, optOut } from '@vskstudio/takt-astro'
const button = document.getElementById('analytics-toggle')
const render = () => {
if (button) button.textContent = isOptedOut() ? 'Activer la mesure' : 'Désactiver la mesure'
}
button?.addEventListener('click', () => {
if (isOptedOut()) optIn()
else optOut()
render()
})
render()
</script>