examples/vite-react-spa (React 19, React Router 8 in data
mode, Vite 8), which runs in the browser test matrix.
Where the tag goes
Put the tag inindex.html, not in a component, so it loads once per page load:
index.html unchanged. Check dist/index.html
after vite build once.
Route changes
The tracker sees everyhistory.pushState and replaceState call and every back and forward
navigation, so each route change of React Router (or any other router) is one pageview. Nothing
needs to be added to the router.
Custom events
data-umami-event attributes work on any element React renders. For a call from code, declare
window.umami once so TypeScript knows it:
Hash routers
A router that keeps the route in the fragment (/#/settings) is counted too: fragments that start
with #/ are sent without any query part inside them. Every other fragment is dropped, because a
fragment can carry an OAuth token.
Bundling instead of a tag
To ship the tracker inside your bundle, see npm.md. The script tag is the simpler choice: its version and integrity are fixed in one visible place.What the browser tests check
One pageview per route change, back and forward each counted, each pageview naming the previous route as referrer, one challenge per page load shared by all its events, both kinds of custom event, an external referrer kept on the first pageview, Global Privacy Control, and a tampered tracker refused (tests/e2e/specs).