takt

Solid

SolidJS a un wrapper dédié : @vskstudio/takt-solid (composant, accesseur, click déclaratif, web component), bâti sur le cœur @vskstudio/takt-core.

pnpm add @vskstudio/takt-solid @vskstudio/takt-core

solid-js (^1.8) et @vskstudio/takt-core (>=0.8.1) sont des peer dependencies. C’est une fine couche sûre au SSR (l’init n’a lieu qu’au montage, côté navigateur), entièrement typée, qui ne touche jamais au payload réseau ni aux garanties de vie privée du cœur. Elle expose deux points d’entrée :

  • @vskstudio/takt-solid — le composant <Takt>, l’accesseur useTakt(), createTaktEvent(), le composant <TaktEvent>, les widgets <TaktBadge> / <TaktEmbed>, ainsi que les helpers du cœur réexportés (badgeUrl(), embedUrl(), createStats(), PublicApiError).
  • @vskstudio/takt-solid/element — le web component <takt-analytics> sans Solid, pour du HTML pur.

Composant <Takt> + useTakt()

Place <Takt> une fois près de la racine : il boote l’analytics dans onMount, émet le pageview initial, câble la navigation SPA et publie l’instance dans un store de module. useTakt() la récupère ensuite depuis n’importe où dans l’application — à condition d’appeler l’accesseur après ce montage.

import type { JSX } from 'solid-js'
import { Takt } from '@vskstudio/takt-solid'

export function App(props: { children: JSX.Element }) {
  return (
    <Takt domain="exemple.fr" outbound files={['pdf', 'zip']}>
      {props.children}
    </Takt>
  )
}

Appelle useTakt() dans le gestionnaire, pas dans le corps du composant : le corps d’un descendant s’exécute avant le onMount de <Takt>, donc une instance capturée là resterait le no-op à vie et tous les events partiraient dans le vide. Le gestionnaire de clic, un createEffect ou createTaktEvent() résolvent l’instance après le montage.

import { useTakt } from '@vskstudio/takt-solid'

export function SignupButton() {
  return (
    <button
      onClick={() =>
        useTakt().track('Signup', {
          props: { plan: 'pro' },
          revenue: { amount: '29.00', currency: 'EUR' }
        })
      }
    >
      S'inscrire
    </button>
  )
}

Props du composant <Takt> :

PropTypeDéfautRôle
domainstringlocation.hostnameIdentifiant du site
endpointstringhttps://taktlytics.com/api/eventURL d’ingestion. Passe /api/event pour un proxy first-party same-origin.
scriptOriginstringOrigine first-party dont dériver l’endpoint (l’origine suivie de /api/event). endpoint l’emporte.
outboundbooleanfalseSuit les liens sortants
filesboolean \| string[]falseSuit les téléchargements (liste d’extensions optionnelle)
spabooleantrueSuit la navigation client (pageviews auto)
track404booleanfalseSignale un événement 404 sur les pages d’erreur (marqueur [data-takt-404] / <meta name="takt:404">, ou statut HTTP 404).
taggedbooleanfalseSuit automatiquement les clics sur les éléments [data-takt-event]
respectDntbooleantrueRespecte Do Not Track
excludeLocalhostbooleantrueIgnore localhost / IP privées
enabledbooleantrueInterrupteur général — false coupe toute mesure
sampleRatenumber1Fraction de sessions échantillonnées (0–1)
trackQuerybooleanfalseConserve la query string dans les URL
queryParamsstring[]Paramètres conservés quand trackQuery est false (liste blanche)
excludestring[]Préfixes de chemin jamais suivis. Bornés au segment : /app couvre /app et /app/… mais pas /application.
scrubUrl(url: string) => stringTransforme chaque URL avant envoi

Les props de config sont lues une seule fois au montage de <Takt> — les changer ensuite n’a aucun effet ; remonte le composant pour reconfigurer.

Le composant transmet toute cette configuration au cœur : les réglages avancés (enabled, sampleRate, trackQuery, queryParams, exclude, scrubUrl) sont disponibles en props, sans instancier le cœur à côté. Seul debug n'est pas exposé : pour l'activer, instancie le cœur toi-même avec createTakt() — et n'utilise pas <Takt> en même temps, sous peine de doubler les pageviews.

useTakt() ne lève jamais : appelé hors d’un <Takt>, pendant le SSR, ou avant que <Takt> ne soit monté, il renvoie une instance no-op qui n’émet rien sur le réseau et avertit une fois en console (useTakt() called before <Takt> mounted). Ce message est le symptôme du piège ci-dessus.

createTaktEvent() + <TaktEvent>

Pour du tracking de clic déclaratif, deux voies qui résolvent l’instance active au moment du clic (pas de closure périmée), avec repli sur l’instance par défaut du cœur.

createTaktEvent() renvoie un { onClick } que tu étales sur n’importe quel élément :

import { createTaktEvent } from '@vskstudio/takt-solid'

export function BuyButton() {
  const onBuy = createTaktEvent({
    name: 'Purchase',
    revenue: { amount: '29.00', currency: 'EUR' }
  })
  return <button {...onBuy}>Acheter</button>
}

<TaktEvent> enveloppe un unique enfant et compose son onClick existant, pour annoter un élément sans toucher à son handler :

import { TaktEvent } from '@vskstudio/takt-solid'

export function SignupCta(props: { onClick: () => void }) {
  return (
    <TaktEvent name="Signup" props={{ plan: 'pro' }}>
      <button onClick={props.onClick}>S'inscrire</button>
    </TaktEvent>
  )
}

Le onClick d’origine de l’enfant se déclenche toujours ; le tracking tourne en parallèle. L’enfant doit rendre un véritable élément DOM : sinon le tracking est désactivé, avec un avertissement en console.

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-solid'

export 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. Le client createStats() réexporté du cœur sert les mêmes statistiques publiques en JSON.

Web component <takt-analytics>

Pour du HTML pur ou un framework non-Solid, importe le sous-chemin ./element (auto-enregistré, embarque le cœur, aucun runtime Solid) :

import '@vskstudio/takt-solid/element'
<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, track-404, tagged.
  • Actifs par défaut, mets ="false" (ou ="0") pour les couper : spa, respect-dnt, exclude-localhost.
  • Valeurs : domain, endpoint, script-origin, sample-rate, ainsi que query-params et exclude qui prennent une liste séparée par des virgules. enabled et track-query ne sont lus qu’en présence de l’attribut, et se coupent avec ="false" / ="0".
<takt-analytics domain="exemple.fr" spa="false" respect-dnt="false"></takt-analytics>

files ne prend pas de liste d’extensions ici, et scrubUrl n’a pas d’équivalent : ce sont des valeurs que seul le composant <Takt> reçoit en props.

<Takt> boote dans onMount et est gardé par isServer de Solid : rien ne touche window/document côté serveur, et useTakt() renvoie le no-op pendant la passe SSR. Importer ./element côté serveur est un no-op tant que customElements n'existe pas.