Blazor is four different hosting arrangements wearing one name, and the snippet goes in a different file in each. The rule underneath them is the same: it belongs in the shell that survives navigation, never in a page.
This site is a Blazor app, and it is measured by Skomi this way.
Get the snippet from your dashboard first:
<script src="https://t.skomi.com/s/YOUR-SITE-ID.js" defer></script>
Where it goes
| Your project | File | Where in it |
|---|---|---|
| Blazor Web App (.NET 8+) | Components/App.razor | In <body>, after <Routes /> |
| Blazor WebAssembly standalone | wwwroot/index.html | In <body>, before the framework script |
| Blazor Server (.NET 6/7) | Pages/_Host.cshtml or Pages/_Layout.cshtml | In <body> |
| MAUI Blazor Hybrid | n/a | Not this guide: that is an app, see installing on MAUI |
For a Blazor Web App it looks like this:
<body>
<Routes />
<script src="https://t.skomi.com/s/YOUR-SITE-ID.js" defer></script>
<script src="_framework/blazor.web.js"></script>
</body>
Why not in a layout or a page
⚠ A script inserted by enhanced navigation is not executed. Blazor patches the DOM rather than reloading the document, and a <script> tag that arrives as part of that patch does not run. So a snippet placed in MainLayout.razor or in a page component works on the first load, is silently dead after the first internal navigation, and looks installed the whole time.
App.razor is outside everything Blazor replaces. The tag loads once, and stays loaded.
Navigation is counted for you
Two different things happen depending on whether you ship the Blazor runtime, and both are already handled.
With blazor.web.js. Internal links use enhanced navigation: the document survives and the address changes through history.pushState. Skomi's script wraps pushState and replaceState and listens for popstate, so each of those is a page view. Nothing to subscribe to, nothing to call on navigation.
Without it. A Blazor app rendered statically with no interactivity ships no runtime at all, so every link is an ordinary page load and counts like any other site. This site is that case.
You do not have to know which one you are: the same tag in the same place is right for both.
⚠ A navigation that does not change the address is not counted, in either mode. Tab strips, accordions and query-less filter changes are interactions, not page views. If you want them measured, send an event.
Interactive render modes change nothing here
InteractiveServer, InteractiveWebAssembly and InteractiveAuto decide where your components run, not how the page is delivered to the browser. The snippet sits above all of it in App.razor and behaves identically under each. Per-component render modes are likewise irrelevant, because there is one document and one script.
If nothing arrives
- Is the tag in
App.razorrather than a layout? See the warning above; this is the failure this guide exists for. - Is it inside
<body>? In<head>it still works, but keep it out of<HeadOutlet />'s way. That outlet is for content components contribute, and a tracker is not one. dotnet watchand a cached document. Enhanced navigation and hot reload can leave you looking at a page that was built before you added the tag. Hard refresh once.- Localhost is not counted. Deploy it, or test against a real hostname.
See the snippet for what it does once it loads, and tracking an app if what you are measuring is software people install rather than a site they visit.