Zum Inhalt springen
Daniel Gietmann
← Blog
Coding

Umami in React einbinden, ohne dass das Tracking still ausfällt

Mit umami-react bindest du Umami als Komponente in eine React-App ein. Vier Stellen, an denen nichts gezählt wird, ohne dass eine Fehlermeldung kommt.

Titelbild: Umami in React einbinden, ohne dass das Tracking still ausfällt

Umami ist eine Webanalyse, die ohne Cookies auskommt und die du selbst hosten kannst. Eingebunden wird sie mit einem einzigen Script-Tag. In einer React-App verwalte ich dieses Tag lieber an einer Stelle im Komponentenbaum, mit Konfiguration aus Props und ohne Tracking während der Entwicklung.

Dafür habe ich umami-react geschrieben, auf npm als @danielgtmn/umami-react. Die Komponente rendert nichts. Sie hängt nur das Umami-Script an document.head und setzt die nötigen Attribute.

Der einfache Fall

pnpm add @danielgtmn/umami-react
import UmamiAnalytics from '@danielgtmn/umami-react';

export function App() {
  return (
    <>
      <UmamiAnalytics
        url="https://analytics.example.com"
        websiteId="deine-website-id"
      />
      {/* restliche App */}
    </>
  );
}

Daraus wird <script defer src="https://analytics.example.com/script.js" data-website-id="…">. Ab da zählt Umami Seitenaufrufe selbst, auch bei Navigation innerhalb einer Single-Page-App.

Das Tückische an Analytics ist, dass ein Fehler nicht auffällt. Die Seite funktioniert, nur das Dashboard bleibt leer. Vier Stellen sind mir dabei begegnet.

1. Lokal wird absichtlich nichts gezählt

onlyInProduction steht standardmäßig auf true. Ist NODE_ENV gleich development, lädt die Komponente das Script gar nicht. Das ist gewollt, damit deine eigenen Testaufrufe nicht in der Statistik landen. Es heißt aber auch: Im Dev-Server siehst du im Network-Tab keine Anfrage an Umami, und das ist kein Fehler.

Willst du das Tracking lokal testen, setz die Prop vorübergehend auf false und leg dir in Umami am besten eine eigene Test-Website an.

2. Ohne debug gibt es keine Fehlermeldung

Fehlen url oder websiteId, lädt die Komponente nichts. Sie meldet das aber nur in der Konsole, wenn debug aktiv ist. Beim Einrichten lohnt es sich deshalb, debug einmal einzuschalten:

<UmamiAnalytics url="…" websiteId="…" debug />

Dann steht in der Konsole, mit welcher Konfiguration die Komponente startet, ob das Script geladen wurde oder warum nicht. Danach wieder ausschalten.

3. Umgebungsvariablen kommen im Browser oft nicht an

Laut README kann die Komponente url und websiteId auch aus UMAMI_URL und UMAMI_ID lesen. Im Code ist das ein Zugriff auf process.env. Ob der im Browser etwas liefert, hängt vom Bundler ab. Next.js gibt im Client nur Variablen mit dem Präfix NEXT_PUBLIC_ weiter, Vite nur solche mit VITE_ über import.meta.env. Eine Variable namens UMAMI_ID ist dort im Browser schlicht leer, und dann greift Punkt 2.

Sicherer ist, die Werte selbst als Props zu übergeben. Im Next.js App Router gehört das in eine Client-Komponente, dazu weiter unten mehr:

// Next.js
<UmamiAnalytics
  url={process.env.NEXT_PUBLIC_UMAMI_URL}
  websiteId={process.env.NEXT_PUBLIC_UMAMI_ID}
/>

// Vite
<UmamiAnalytics
  url={import.meta.env.VITE_UMAMI_URL}
  websiteId={import.meta.env.VITE_UMAMI_ID}
/>

Die Website-ID ist kein Geheimnis. Sie landet ohnehin im Browser, im Bundle und als data-website-id am Script-Tag.

4. lazyLoad zählt nur Besucher, die etwas tun

Mit lazyLoad lädt das Script erst bei der ersten Mausbewegung, dem ersten Scrollen, Klicken oder Tastendruck. Das hält das Script aus dem ersten Laden der Seite heraus. Dafür taucht in der Statistik nie auf, wer die Seite öffnet, liest, ohne zu scrollen, und wieder geht. Gerade auf kurzen Seiten oder auf dem Handy ist das kein Randfall.

Da das Script ohnehin mit defer geladen wird und klein ist, lasse ich lazyLoad aus. Wer es einschaltet, sollte wissen, dass die Besucherzahlen danach niedriger und die Absprungrate besser aussehen, als sie sind.

Next.js mit App Router

Die Komponente nutzt useEffect, das veröffentlichte Paket trägt aber keine 'use client'-Direktive. Direkt in einem Server-Layout wie app/layout.tsx importiert, beschwert sich Next.js deshalb. Eine kleine Client-Komponente dazwischen löst das:

// app/analytics.tsx
'use client';

import UmamiAnalytics from '@danielgtmn/umami-react';

export function Analytics() {
  return (
    <UmamiAnalytics
      url={process.env.NEXT_PUBLIC_UMAMI_URL}
      websiteId={process.env.NEXT_PUBLIC_UMAMI_ID}
    />
  );
}
// app/layout.tsx
import { Analytics } from './analytics';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="de">
      <body>
        <Analytics />
        {children}
      </body>
    </html>
  );
}

Eigene Events

Für Klicks auf Buttons oder abgeschickte Formulare gibt es den Hook useUmami. Im Next.js App Router braucht die Datei mit dem Button ebenfalls 'use client' ganz oben, weil sie einen Hook und onClick nutzt:

'use client';

import { useUmami } from '@danielgtmn/umami-react';

export function KontaktButton() {
  const { track } = useUmami();

  return (
    <button onClick={() => track('kontakt-klick', { ort: 'header' })}>
      Kontakt
    </button>
  );
}

track ruft window.umami.track auf, sobald das Script geladen ist. Davor passiert nichts, auch kein Fehler. Ein Klick in den ersten Millisekunden nach dem Laden, oder bei lazyLoad vor der ersten Interaktion, geht also verloren. Für Events wie „Formular abgeschickt“ ist das selten ein Problem, für „Seite geöffnet“ schon. Das zählt Umami ohnehin selbst.

UTM-Parameter gehören in die URL

Umami liest utm_source, utm_medium und die anderen UTM-Werte aus der aufgerufenen URL. Ein Link wie https://example.com/?utm_source=newsletter&utm_medium=email reicht, damit die Kampagne im Dashboard auftaucht, ohne eine Zeile Code. Diesen Weg würde ich immer zuerst nehmen.

Prüfen, ob es läuft

Öffne die gebaute Seite, nicht den Dev-Server, und schau in den Network-Tab. Du solltest zwei Anfragen sehen: script.js von deiner Umami-Instanz und danach einen POST an /api/send. Antwortet der mit einem Fehler, steht der Grund im Response-Body. Kommt keine der beiden Anfragen, schalte debug ein und lies die Konsole.

In Umami selbst zeigt die Echtzeitansicht deinen eigenen Besuch nach wenigen Sekunden.