Add analytics snippets and hook into SPA route changes.
#Analytics
Add analytics to a Krate site with a snippet in a layout and the __krate_onNavigation hook for SPA route changes.
#Injecting the snippet
Put the embed script in a layout (or any component) so it is present on every page:
// src/layouts/RootLayout.tsx
export default function RootLayout({ children }) {
return (
<>
<Script src="https://plausible.io/js/script.js" data-domain="mysite.com" />
<div>{children}</div>
</>
);
}<Script> forwards all attributes, so flags like defer and data-domain work as expected. If your provider needs an inline init snippet, use the inline form — children are written raw and never escaped:
<Script>{`gtag('config', 'G-XXXXXXXXXX');`}</Script>#Tracking SPA navigations
Krate's client router swaps content in place, so an ordinary tag-based snippet only fires on the initial full page load. Register a navigation hook once — for example in a layout script or the root page — and it runs on every successful SPA navigation:
<Script>{`
window.__krate_onNavigation = (url) => {
if (window.plausible) window.plausible('pageview', { u: url });
};
`}</Script>For Google Analytics:
<Script>{`
window.__krate_onNavigation = (url) => {
if (window.gtag) window.gtag('event', 'page_view', {
page_location: url,
page_title: document.title,
});
};
`}</Script>Hook contract:
- Signature —
(url: string) => void;urlis the resolved navigation target (absolute), the same URL passed to thekrate:navigateCustomEvent. - Fires once per navigation — click, prefetch-cache hit, back/forward, and error-page replacements. The initial page load is a full page load and is tracked by the snippet's usual beacon — do not double-count it.
- Fail-safe — a throw inside the hook is caught and ignored so analytics can never break routing.
- Set before navigating — assign it once at app bootstrap (e.g. in a layout
<Script>); it persists across navigations.
#Alternatives
The same event is also available as a DOM event, which some analytics wrappers consume directly:
window.addEventListener('krate:navigate', (e) => {
// e.detail.url — the navigated-to URL
});Last updated: 2026-10-16
Contributors: Noinkin