Nuxt integration
Nuxt analytics, error tracking and uptime
BugOtter works with Nuxt, server-rendered or generated. Add one entry to app.head in nuxt.config.ts for cookieless analytics and browser errors. @sentry/nuxt, pointed at your BugOtter DSN, catches server errors. Uptime and a status page start when you add the URL.
- 1 entryin app.head
- SSR or generateboth work
- 3 regionsuptime checks
What BugOtter gives a Nuxt app
Analytics with no banner
One entry innuxt.config.ts, on server pages and generated ones alike.Server errors from Nitro
@sentry/nuxtwith your BugOtter DSN. Nothing added to the browser bundle.Past ad blockers
ThreerouteRulesforward the script and beacons from your own domain.
Analytics: one entry in app.head
Add the script in nuxt.config.ts
Nuxt renders
app.headinto every page, on the server and innuxt generateoutput. Quoted keys like"data-app"come out as attributes on the tag. No cookies, no banner.nuxt.config.ts // nuxt.config.ts export default defineNuxtConfig({ app: { head: { script: [ { src: "https://bugotter.com/bo.js", defer: true, "data-app": "YOUR_APP_ID" }, ], }, }, });Route changes are page views
Nuxt's router changes pages with
history.pushState, which the script follows. Each new path counts once, with its own time on page, scroll depth and Web Vitals. Keep private pages out with"data-exclude": "/dashboard"in the same object.Goals from components
Call
window.bo?.track()in an event handler, never at the top of<script setup>: that part also runs on the server, where there's nowindow. Forms, file downloads and outbound links are counted with no code.app/components/UpgradeButton.vue <!-- app/components/UpgradeButton.vue --> <script setup lang="ts"> const props = defineProps<{ plan: string }>(); function upgrade() { window.bo?.track("upgrade_click", { plan: props.plan }); } </script> <template> <button @click="upgrade">Upgrade</button> </template>Using TypeScript? Declare
window.boonce, sotrack()type-checks:bo.d.ts declare global { interface Window { bo?: { track(name: string, props?: Record<string, string | number | boolean>): void; visitorId(): string | null; }; } } export {};
npx bugotter init --token bo_… writes your keys (BUGOTTER_DSN, BUGOTTER_PROJECT, BUGOTTER_KEY) to .env.local, or to .env if that's the file you have. Nuxt reads .env in development, so keep them there. It prints the tag for you to place in nuxt.config.ts.

- Payments on the chart. A green marker shows the day a big payment came in.
- Error spikes, too. A red marker lines up a bad day with the error behind it.
- Last period, dashed, so a drop or a launch spike is obvious at a glance.
- Bots kept out of every number, with the count one click away.
Get past ad blockers with routeRules
Nitro, the server inside Nuxt, can forward three paths to BugOtter from your own domain.
20–40%
of visitors are dropped by ad blockers for most analytics
// nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
"/_bo/bo.js": { proxy: "https://bugotter.com/bo.js" },
"/_bo/api/collect": { proxy: "https://bugotter.com/api/collect?via=YOUR_PROXY_KEY" },
"/_bo/api/errors": { proxy: "https://bugotter.com/api/errors?via=YOUR_PROXY_KEY" },
},
app: {
head: {
script: [{ src: "/_bo/bo.js", defer: true, "data-app": "YOUR_APP_ID" }],
},
},
});- One rule per path, as above. A wildcard rule (
/_bo/**) can't carry the?via=key, and the key is what proves visits came through your proxy. - Nitro passes on the request body and the browser's headers, which is what BugOtter checks.
- This needs a server build. On
nuxt generate, use your host's rewrites (Netlify, Vercel, Cloudflare).
Check it once: curl -sI https://yourapp.com/_bo/bo.js should answer 200. Your proxy key is on the app's Traffic tab in BugOtter. Keep the ?via= part: it proves visits came through your proxy, and it only lives in server config, never in the page.
Errors: @sentry/nuxt on the server, sent to BugOtter
Browser errors come from the script. For server routes, API handlers and server-side rendering, use Sentry's Nuxt module with your BugOtter DSN. No Sentry account needed.
Install the module
terminal npm install @sentry/nuxtAdd it, without Sentry's source map upload
nuxt.config.ts // nuxt.config.ts export default defineNuxtConfig({ modules: ["@sentry/nuxt/module"], sentry: { sourcemaps: { disable: true }, // maps go to BugOtter, not Sentry telemetry: false, }, sourcemap: { client: "hidden" }, });Init it with your DSN, server only
sentry.server.config.ts // sentry.server.config.ts (project root) import * as Sentry from "@sentry/nuxt"; Sentry.init({ dsn: process.env.BUGOTTER_DSN, // process.env: runtime config isn't ready this early dataCollection: { userInfo: false, cookies: false, httpBodies: [], databaseQueryData: false }, });The
dataCollectionline is for SDK v11; on v10 usesendDefaultPii: false. Leave outtracesSampleRate: BugOtter keeps errors, not traces. Don't addsentry.client.config.ts.
Kept
- Exceptions and
captureMessageevents, grouped into issues - Release, environment, the request path and runtime
- Navigation and failed-request breadcrumbs
- Your user id, hashed (never stored raw)
Accepted, then dropped
- Performance traces and spans
- Session replays and profiles
- Sessions, logs and metrics
- Console breadcrumbs and click text
On the Cloudflare presets, sentry.server.config.ts isn't used, and Sentry's Cloudflare Nitro plugin takes its place (see Sentry's Nuxt deployment notes). Node, Vercel and Netlify presets work as shown.

- A plain summary written from the evidence: page, browser, release and visits.
- Fix with agent hands the whole issue to Claude Code, Cursor or Codex.
- Where your code broke, mapped back to your file and line with your source maps.
Source maps from your Nitro output
sourcemap: { client: "hidden" } (in the config above) writes client maps without a link to them in your scripts. Upload them after nuxt build from the folder your preset publishes, so --delete removes them from what goes live:
| Preset | Upload from |
|---|---|
Node server, nuxt generate | .output/public |
| Vercel | .vercel/output/static |
| Netlify, Cloudflare Pages | dist |
"build": "nuxt build && npx bugotter sourcemaps .output/public --delete"Bundles land under /_nuxt/, as the browser loads them. The CLI needs BUGOTTER_PROJECT and BUGOTTER_KEY (the upload key; keep it in CI, never in runtimeConfig.public). The source maps guide covers releases.
Uptime, a status page and deploys
3 regions
check your app from the moment you add the URL
Add one more check on a server route that reaches your database, and expect ok to be true:
// server/api/health.get.ts
export default defineEventHandler(async () => {
await useDatabase().sql`select 1`; // or your own client: prove the database answers
return { ok: true };
});Nitro scheduled tasks or an outside cron can call a BugOtter heartbeat URL when they finish, so a run that's late or fails alerts you.
Deploys: automatic on Vercel once connected. On Netlify, add your app's deploy webhook as a Deploy succeeded notification. Elsewhere, run npx bugotter deploy as the last step.
Status page: at bugotter.com/status/your-app, with a README badge. Pro can use your own domain.

- Every day at a glance. Amber means a short outage that day.
- Response time, median and p95, and the slowest region.
- Three regions check your URL. An outage only counts when two agree.
Or ask your coding agent
Connect BugOtter to Claude Code, Cursor or Codex once over MCP. It signs in through your browser, read-only unless you allow changes. Then paste one line.
claude mcp add --transport http --scope user bugotter https://bugotter.com/api/mcpThen ask, in plain words:
Add BugOtter to this Nuxt app: the script in app.head, the three routeRules proxy rules, and @sentry/nuxt on the server only with my BugOtter DSN.
Using BugOtter, list this app's server errors since the last deploy and fix the newest one.
Add a /api/health server route that checks the database, then add it as an uptime check in BugOtter.
Cursor, VS Code, Codex and the rest have their own one-step setup at Docs: your agent (MCP). Every setup screen in BugOtter also has a Copy prompt for your agent button with your keys filled in.
Questions
Does BugOtter count Nuxt page changes?
Yes. Nuxt uses vue-router's web history, which changes pages with history.pushState. The script listens for that and for the back button, so each new path is one page view. router.replace() swaps the entry instead, so it isn't counted as a new page view.
Why app.head in nuxt.config and not useHead in app.vue?
Both render the tag into the HTML. app.head in nuxt.config is set once for the whole app and ends up in pages made by nuxt generate too, so it's the simplest place. If you prefer useHead, call it in app.vue, so the tag is on every page.
Can I use the routeRules proxy with nuxt generate?
No. Route rule proxies run in Nitro, the Nuxt server, and a fully static site has none. Use your host's rewrites instead: Netlify, Vercel, or a Cloudflare Pages Function. A server build on any Nitro preset can use routeRules.
Do I still need node --import for @sentry/nuxt?
Not on SDK v11: the module bundles sentry.server.config.ts into the server build and starts it on boot, which is why it works on Vercel and Netlify too. On v10 and older, start the server with node --import ./.output/server/sentry.server.config.mjs .output/server/index.mjs.
Should I add sentry.client.config.ts?
No. The script already catches browser errors and failed promises, with the pages and failed requests before each one. Without a client config file, the module adds nothing to the browser bundle.
Where is the status page?
At bugotter.com/status/your-app once you turn it on, with uptime and incidents only. Pro can serve it from your own domain.
Sources
Checked Nov 4, 2026 against the Nuxt config reference (app.head, sourcemap), Nitro route rules, and Sentry's Nuxt install guide. The sourcemaps.disable option is from the @sentry/nuxt 11.10 module source. BugOtter isn't affiliated with Sentry or Nuxt.
Watch every app you ship, from one calm list.
14 days of everything, no card needed. Plans from $10 a month, billed yearly.