Install guide
Add website analytics to Vue
index.html, in the head. Hash-mode routers need one more attribute.
The tag goes in index.html, in <head> - the HTML file Vite serves, not a component.
<script defer src="https://app.feasible.lol/js/fs-k7m2q4x5r3n6t2v5.js"></script>Steps
- Open
index.htmlin your project root, next topackage.json. - Paste the tag inside
<head>, above the<script type="module" src="/src/main.ts">line. - Rebuild and deploy.
On a Vue CLI project the file is public/index.html instead. Same place inside it.
Nuxt is different - the tag goes in nuxt.config.ts. See the Nuxt guide.
Hash mode is the Vue gotcha
If your router is built with createWebHashHistory(), every route lives in the fragment:
https://yourdomain.com/#/pricing
https://yourdomain.com/#/about
Fragments aren’t part of the reported URL by default, so every one of those pages reports as /. Your top-pages report becomes a single row and your best-performing page is unknowable.
Fix it with one attribute:
<script defer data-hash="true"
src="https://app.feasible.lol/js/fs-k7m2q4x5r3n6t2v5.js"></script>
A bare data-hash does nothing - it needs a value. Any non-empty value counts as on, including the string "false", which is a trap worth knowing about. Script options has the full list.
If you’re on createWebHistory() - real paths, no # - ignore all of this. It just works.
Don’t add it in a component or a router guard
The script listens for pushState, replaceState, popstate and hashchange itself, and counts one pageview per route change. Two changes in the same tick collapse to one event at the final URL, so a navigation guard that redirects doesn’t count twice.
So there’s nothing to put in router.afterEach(). If you add a manual pageview call there on top of the automatic one, every number on the site doubles - and a doubled number is worse than no number, because you’ll believe it.
What comes for free
Custom events
One call, from anywhere in your app:
window.feasible("Signup", { props: { plan: "annual" } });
Properties are allow-listed per site before they show up in reports, and values that didn’t carry the property land in a labeled (none) bucket rather than vanishing. See custom properties.
Check it worked
Build and deploy, or run a production preview. localhost is not counted, so npm run dev shows nothing however correct your tag is.
Open the site, click a link to a second route rather than reloading, and watch the network tab for one POST to /api/event per route. Then open Feasible.
You should show up under Real-time visitors. If you don’t, go to Site settings → Ingestion health. It counts every event that arrived and every one that was dropped, each with a named reason - unknown_site means the snippet is for a site you haven’t registered, hostname_not_allowed means the page is on a hostname that isn’t on your list.
The send a test event button there posts through the real public URL, so it exercises what a browser does.
A brand-new site takes about fifteen seconds before its first event is accepted, so wait that out first.
Related: Nuxt, React, and the install docs.