Session Replay and Heatmaps for TanStack Start and TanStack Router
Add session replay and heatmaps to a TanStack Start application. Covers placing the loader in the root shell, why a page component is the wrong place, automatic route tracking, and notes for prerendered sites.
Short answer: put the loader script in the shell component rendered by your root route, in src/routes/__root.tsx. The shell is rendered once on the server and is not re-rendered during client-side navigation, so the tag is never duplicated. A page component is the wrong place, because it would inject the script again on every route change. Route changes are tracked automatically.
We run our own marketing site on TanStack Start, so this is the setup we use ourselves.
Table of Contents
- Install in the Root Shell
- Why Not a Page Component
- Routing
- Identify and Track
- Notes for Prerendered Sites
- Key Takeaways
Install in the Root Shell
// src/routes/__root.tsx
function RootShell({ children }: { children: ReactNode }) {
return (
<html lang="en">
<head>
<HeadContent />
</head>
<body>
{children}
<script src="YOUR_LOADER_URL" defer />
<Scripts />
</body>
</html>
);
}Your loader URL is in the project's install settings. defer loads the script without blocking rendering. Reference: Install in TanStack Start.
Why Not a Page Component
It is tempting to add the script in the component for your index route, or inside a layout that re-renders.
- A route component mounts again on navigation. A script tag inside it is injected each time.
- Duplicate loaders mean duplicate initialisation and, potentially, duplicate events.
- A script tag in a single route covers only that route.
The root shell exists once for the lifetime of the page. That is the property you want.
If you only use TanStack Router in a client-rendered app, without Start, put the tag in index.html as for any React single-page app (React guide).
Routing
The SDK patches pushState and replaceState. TanStack Router navigations are recorded as page views without any configuration.
- File-based routes appear with their real paths.
- Dynamic segments such as
/blog/$idappear with actual values. - Search-parameter changes update the URL. Decide whether you want them treated as separate pages when you analyse, and keep personal data out of search parameters (PII in URLs).
Steps that do not change the URL need a custom event.
Identify and Track
Server functions and loaders run on the server. The SDK exists only in the browser. Call it from client-side code:
function trackEvent(name, props) {
if (typeof window !== "undefined" && typeof window.deepsync === "function") {
window.deepsync("track", name, props);
}
}Identify once the user is known:
window.deepsync("identify", user.id, { plan: user.plan });Guard against the SDK not being ready, or add the command-queue stub from how the SDK works so early calls are buffered.
Mask rendered personal data with classes, and wire consent if your project requires it (PII masking, consent modes).
Notes for Prerendered Sites
TanStack Start can prerender routes to static HTML, which is how many marketing sites are deployed. Three things we learned running one.
1. The tag is in every prerendered page. Because it lives in the root shell, each generated HTML file includes it. Nothing more to do.
2. Check how your host serves the files. Prerendering writes each route as a folder with an index file. Some static hosts then serve the URL with a trailing slash and redirect the version without. If your canonical tags and sitemap use the non-slash form, every page points at a redirect. We hit exactly this, and fixed it with one hosting setting (the trailing-slash bug). It also matters for analytics: a redirect can drop campaign parameters or change the recorded landing URL.
3. Server routes are separate from pages. Routes that return data, such as a sitemap, are not HTML and do not carry the script. That is correct.
Also check your Content Security Policy if you set one: allow the ingest origin in script-src and connect-src (CSP guide).
Key Takeaways
- Put the loader in the root shell, once.
- Navigation is tracked automatically.
- Call the SDK only from browser code.
- On prerendered sites, check trailing-slash handling at your host.
Conclusion
TanStack Start's root shell gives you one correct place for a script. Use it, and the rest works without further setup.
Frequently Asked Questions
Related articles
Stay in the loop
Get the latest insights on product analytics and user behavior delivered to your inbox.



