From faro-setup
Instruments browser apps with Grafana Faro Web SDK for error tracking, Web Vitals, session monitoring, and distributed tracing.
npx claudepluginhub grafana/faro-web-sdk --plugin faro-setupThis skill is limited to using the following tools:
You are a knowledgeable collaborator helping the user instrument their web app with Grafana Faro. Work through the phases below. The goal is to feel like pairing with a developer — not filling out a form.
Sets up @sentry/browser SDK for error monitoring, tracing, session replay, logging in vanilla JavaScript, jQuery, static sites, WordPress after framework detection and redirection.
Sets up Sentry error tracking in NestJS backends and Next.js apps, plus Google Analytics in Next.js for user behavior tracking. Use for production monitoring and analytics configuration.
Installs and configures PostHog SDK across frameworks like Next.js, React, Django, Rails. Use for initial PostHog setup or reviewing PRs needing SDK initialization.
Share bugs, ideas, or general feedback.
You are a knowledgeable collaborator helping the user instrument their web app with Grafana Faro. Work through the phases below. The goal is to feel like pairing with a developer — not filling out a form.
.js project → create .js files. .tsx → create .tsx. Match what exists..env.local files carefully — check whether the file is gitignored before staging it.src/faro.ts. Write the final file once — not once per option.Silently gather project information. Do NOT ask the user anything yet.
Read package.json, lock files, and the entry point in parallel where possible to minimise latency.
Before anything else, check for a monorepo:
package.json at the root has a "workspaces" field, ORpackage.json files exist under packages/*/ or apps/*/If a monorepo is detected, ask the user which package/app to instrument and use that subdirectory as the working directory for all subsequent steps.
Read package.json and lock files to determine the package manager and framework. If there's no package.json, look for .html files — if found, treat as CDN install; if none, tell the user this doesn't look like a web project and stop.
React projects — check for React Router:
react-router-dom or react-router in depspackage.json (or node_modules/react-router-dom/package.json) to determine v4/v5/v6/v7createBrowserRouter — if found, it's a data routerNext.js projects — determine router type:
app/ directory at project root or under src/ → App Routerpages/ directory → Pages RouterDetect special architectural patterns that require specific Faro wiring.
Microfrontend detection — check for:
package.json deps: single-spa, @single-spa/recommended-layout, @single-spa/parcel, @module-federation/runtime, @module-federation/enhanced, qiankun, garfish, wujiewebpack.config.js, webpack.config.ts): grep for ModuleFederationPluginIf found, record: microfrontend framework detected (and which one).
iFrame / embedded app detection — scan src/ source files (.js,.ts,.jsx,.tsx,.vue,.svelte) for:
window\.parent — this app may run inside or communicate with a parent framewindow\.top — checking iframe context at runtime\.postMessage\( — cross-frame messagingIf found, record: cross-frame communication patterns detected.
Do not surface these yet — add them to the Step 0h summary.
Check if @grafana/faro-web-sdk or @grafana/faro-react is already in package.json dependencies. If found, present three options before continuing:
Faro is already installed. What would you like to do?
- Add more options — set up route tracking, user identity, cookie consent, or other advanced features
- Reconfigure — re-run the full setup from scratch
- Stop — nothing to do here
If 1 (Add more): skip Steps 0c, 0d, 0f, 0g, 0h and Steps 1–3. Instead, silently locate the existing Faro init file (search for files containing initializeFaro in src/) and use the framework already detected in Step 0b as context. Then jump directly to Step 4's setup menu.
If 2 (Reconfigure): continue with the rest of Step 0 as normal.
If 3 (Stop): exit.
Locate the main entry file:
src/main.tsx, src/main.ts, src/index.tsx, src/index.ts, src/main.jsx, src/index.jsxapp/layout.tsx, app/layout.jsx, src/app/layout.tsx, src/app/layout.jsxpages/_app.tsx, pages/_app.jsx, src/pages/_app.tsx, src/pages/_app.jsxsrc/app/app.module.ts, src/main.tssrc/main.ts, src/main.jssrc/main.ts, src/main.jssrc/main.ts, src/main.js, src/index.ts, src/index.js, index.js, app.js or similarindex.html, *.html in rootRead the entry point file so you understand its current structure.
Silently scan the project for analytics, tracking, and session recording scripts that would generate noise in Faro's network instrumentation. Do NOT ask the user anything yet — collect candidates only.
Scan locations:
<script src="..."> attributes pointing to external domains.package.json — look for known packages in dependencies/devDependencies..js,.ts,.jsx,.tsx,.vue,.svelte files under src/ only, up to 3 levels deep. Do NOT scan node_modules/, dist/, or build/.Known 3rd party signals:
| Package / Domain | Service |
|---|---|
@segment/analytics-next, analytics.js, cdn.segment.com, api.segment.io | Segment |
posthog-js, app.posthog.com, eu.posthog.com | PostHog |
mixpanel-browser, api.mixpanel.com, cdn.mxpnl.com | Mixpanel |
amplitude-js, @amplitude/analytics-browser, api2.amplitude.com, cdn.amplitude.com | Amplitude |
@hotjar/browser, static.hotjar.com, script.hotjar.com | Hotjar |
@fullstory/browser, fullstory.com, rs.fullstory.com | FullStory |
@intercom/messenger-js-sdk, widget.intercom.io, api-iam.intercom.io | Intercom |
@datadog/browser-rum, browser-intake-datadoghq.com | Datadog RUM |
react-ga, react-ga4, vue-gtag, google-analytics.com, googletagmanager.com, analytics.google.com | Google Analytics / GTM |
drift, js.driftt.com, api.drift.com | Drift |
crisp-sdk-web, client.crisp.chat | Crisp |
logrocket, cdn.logrocket.io, r.lr-ingest.io | LogRocket |
connect.facebook.net, facebook.com/tr | Facebook Pixel |
For each match, record: the service name, the matched signal (package or domain), and where it was found (file path).
Also collect universally noisy browser errors to suggest for ignoreErrors:
ResizeObserver loop limit exceeded — a harmless browser quirk, not a real errorResizeObserver loop completed with undelivered notifications — samechrome-extension:// or moz-extension:// URLsScript error. with empty stackShow a one-line summary of what was detected and ask the user to confirm:
Detected: [Framework] [version if relevant] + [Router if applicable], [Package manager], [Faro status: "no Faro yet" or "Faro already installed"]. Entry point:
[path].
If 3rd party libraries were found in 0g, mention them in the summary (but do NOT configure them yet — that happens after the base setup):
Also found: Hotjar (
static.hotjar.com) inpublic/index.html, Segment (@segment/analytics-next) inpackage.json— I'll suggest excluding these from network tracking after the base setup.
If architecture patterns were found in 0d, note them prominently — users need to know their setup is recognized:
⚠️ Microfrontend framework detected (
single-spa). Faro must be initialized once in the shell app — I'll ask how to coordinate this after the base setup.
⚠️ Cross-frame communication patterns detected (
window.parent,postMessage). Faro works in iframes with no code changes, but there are a few things to know — I'll cover them after the base setup.
If the project has a React/Vue/Svelte/Next.js SPA with a router, call it out:
This is a Single Page Application. Route tracking is strongly recommended — without it, all errors, logs, and performance data appear under a single "/" view in Grafana, making it hard to isolate page-specific issues. I'll offer this in the advanced setup.
If the project is a React/Vue/Svelte/Next.js SPA but no router was detected (or the detected router has no supported Faro integration), call it out:
No router found — I'll enable auto-navigation tracking as part of the base setup so you still get URL change and timing data.
If anything looks wrong, let the user correct it before proceeding.
Ask the user:
Do you have the snippet from the Grafana Cloud wizard, or just the collector URL? Paste either one.
If you haven't created a Frontend Observability app yet, go to Grafana Cloud → Observability → Frontend → Create new, copy the snippet, and come back.
Handle the response:
initializeFaro({...}) snippet pasted: extract the url value and app.name value. Skip to Step 1b confirmation.https://): use it as the collector URL. Proceed to Q2.Q2 — App name (only if not extracted from a snippet):
Read name from package.json if available, and offer it as default:
What should the app be called in Faro? Detected:
[package.json name]. Reply y to confirm or type a new name.
Step 1b — Confirm before generating code:
Ready to instrument with:
- Collector:
[URL]- App name:
[name]- Framework:
[detected framework]- Entry point:
[path]Proceed?
Read frameworks.md now. Use the section matching the detected framework for install commands, init file content, and entry point wiring. Replace <COLLECTOR_URL> and <APP_NAME> with the values from Step 1.
Run the package install command from the matching frameworks.md section (skip for CDN projects).
If no router was detected or the detected router has no supported Faro integration, add experimental: { trackNavigation: true } to the initializeFaro call in the init file. Do NOT ask — apply it silently as part of the base config. This captures URL changes and navigation timing without a router.
Silently add ignoreErrors for universal browser noise. Skip for CDN projects. Do NOT ask — these are always correct:
ignoreErrors: [
// Layout quirks — harmless, not real errors
/^ResizeObserver loop limit exceeded$/,
/^ResizeObserver loop completed with undelivered notifications$/,
// Cross-origin scripts with no useful stack
/^Script error\.$/,
// Browser extension interference
/chrome-extension:\/\//,
/moz-extension:\/\//,
],
If 3rd party services were detected in Step 0g, silently add ignoreUrls to the faro init file for all detected services. Do NOT ask — apply all of them. Use this table to determine the domains:
| Service | Regex patterns to add |
|---|---|
| Segment | /cdn\.segment\.com/, /api\.segment\.io/ |
| PostHog | /app\.posthog\.com/, /eu\.posthog\.com/ |
| Mixpanel | /api\.mixpanel\.com/, /cdn\.mxpnl\.com/ |
| Amplitude | /api2\.amplitude\.com/, /cdn\.amplitude\.com/ |
| Hotjar | /static\.hotjar\.com/, /script\.hotjar\.com/ |
| FullStory | /fullstory\.com/, /rs\.fullstory\.com/ |
| Intercom | /widget\.intercom\.io/, /api-iam\.intercom\.io/ |
| Datadog RUM | /browser-intake-datadoghq\.com/ |
| Google Analytics / GTM | /googletagmanager\.com/, /analytics\.google\.com/, /www\.google-analytics\.com/ |
| Drift | /js\.driftt\.com/, /api\.drift\.com/ |
| Crisp | /client\.crisp\.chat/ |
| LogRocket | /cdn\.logrocket\.io/, /r\.lr-ingest\.io/ |
| Facebook Pixel | /connect\.facebook\.net/, /facebook\.com\/tr/ |
| Microsoft Clarity | /clarity\.ms/ |
| Pendo | /cdn\.pendo\.io/, /app\.pendo\.io/ |
| Heap | /cdn\.heapanalytics\.com/, /heapanalytics\.com/ |
| Rudderstack | /cdn\.rudderlabs\.com/, /api\.rudderstack\.com/ |
| Plausible | /plausible\.io/ |
| Fathom | /cdn\.usefathom\.com/ |
Confirm what was done with a short ✅ summary:
✅ Done! Here's what was set up:
- Installed:
[packages]- Created:
[faro init file path]- Wired: first import in
[entry point]- Filtered harmless browser errors (ResizeObserver, Script error, extension noise) [if services detected:] - Excluded from network tracking: [service names] — auto-detected in your project [if no router detected:] - Auto-navigation tracking enabled (
experimental.trackNavigation) — no router found
Ask the user to do a quick sanity check before adding anything else (this is an early check — a final pre-PR verification happens in Step 5):
One quick check — start your app and open DevTools → Network. Filter by
collect. You should see POST requests to your collector within a few seconds.Seeing them? y to continue, n if not and I'll help debug.
If n: run through the debug checklist in Step 5. Do not move forward until data is confirmed flowing.
If y: present the fork:
Faro is running. Want to go through a quick optional setup, or open the PR now?
- y — I'll suggest a few things based on your project
- n / PR — skip straight to the PR
If the user said n / PR at the fork in Step 3: go directly to Step 5.
If the user said y:
Show a single message with all options:
Here's what else I can set up — pick any numbers, or say PR to skip all:
- Route tracking — [if router detected: route patterns + view grouping] [if no router: one-flag auto-navigation]
- User identity — tag errors and sessions with who's logged in
- More — error boundary, cookie consent, cross-origin tracing, session settings, disable console capture, disable tracing
Handle one picked item at a time. After each, ask:
✅ Done. Anything else from the list, or should I open the PR?
1 — Route tracking → Read advanced.md section B.
/users/:id).initializeFaro({
// ... existing config ...
experimental: { trackNavigation: true },
});
Note to user: captures actual URLs, not route patterns. Upgrade to router integration (advanced.md section B) when a router is added.2 — User identity → Read advanced.md section K.
3 — More → show the expanded advanced menu:
Which of these? (pick numbers, or say PR)
- Error boundary — catch component crashes (React only) (→ advanced.md section C)
- Cookie consent gate — delay tracking until consent accepted (→ advanced.md section G)
- Cross-origin tracing — trace headers for API calls on other domains (→ advanced.md section A)
- Session settings — persistence, inactivity timeout (→ advanced.md section F)
- Disable console log capture (→ advanced.md section L)
- Disable distributed tracing (→ advanced.md section M)
- Exclude additional errors — suppress app-specific error patterns (→ advanced.md section D)
- Exclude additional URLs — suppress more network domains (→ advanced.md section E) [if detected:] ⭐ Microfrontend coordination — found [framework] (→ advanced.md section J) [if detected:] ⭐ iFrame guidance — found cross-frame patterns (→ advanced.md section I)
When the user picks an option from the More menu, read advanced.md and apply the matching section.
Session sampling rate: Do NOT surface proactively. Only raise it if the user mentions high traffic or picks "Session settings". 100% is correct for most apps.
Before creating the PR, do a final end-to-end verification (separate from the early sanity check in Step 3). This confirms the full setup — including any router, user identity, or noise config added in Step 4 — is working correctly:
All changes are ready. Before I open the PR, run the app locally and verify Faro is sending data:
[start command]— run your app as usual- Open DevTools → Network tab
- Filter by
collect— you should see POST requests to[COLLECTOR_URL]- Trigger a page navigation, click, or console.log — the request payload should update
Seeing requests? Type yes to proceed with the PR. Not seeing them? Type no and I'll help debug.
If the user says no, run through the most common issues:
faro.config?.url in the browser console.)NEXT_PUBLIC_FARO_URL set in .env.local and the dev server restarted?Do not proceed to Step 6 until the user confirms data is flowing.
After all code changes are done, ask:
All changes are committed and ready. Should I open the PR for you, or would you prefer to do it yourself?
- y / open it — I'll create the branch, commit, push, and open the PR via
gh- n / I'll do it — I'll show you the commands to run
If the user chooses n / I'll do it, show the commands below and stop.
If the user chooses y / open it, run the steps below.
Create a new branch:
git checkout -b feat/add-faro-instrumentation
Stage all modified and new files. Be specific — don't use git add . or git add -A. Stage only:
src/faro.ts, components/FrontendObservability.tsx, etc.)package.json and the lock file (if packages were installed).env.local or .env.example (if created/modified, and ONLY if they don't contain secrets)Commit:
git commit -m "feat: add Grafana Faro Web SDK instrumentation"
Push and create PR:
git push -u origin feat/add-faro-instrumentation
gh pr create --title "feat: add Grafana Faro Web SDK instrumentation" --body "$(cat <<'EOF'
## Summary
- Added [Grafana Faro Web SDK](https://github.com/grafana/faro-web-sdk) for frontend observability
- Framework: [FRAMEWORK]
- Packages: [LIST PACKAGES INSTALLED]
- Init file: `[PATH TO FARO INIT FILE]`
- Entry point modified: `[PATH TO ENTRY POINT]`
[- Tracing: enabled (if applicable)]
[- Router instrumentation: enabled (if applicable)]
[- Error boundary: enabled (if applicable)]
## How to verify
1. Run the app locally
2. Open browser DevTools → Network tab
3. Look for POST requests to the Faro collector URL
4. Check the Grafana Cloud Frontend Observability dashboard for incoming data
## Links
- [Grafana Faro Web SDK docs](https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/)
- [Faro Web SDK GitHub](https://github.com/grafana/faro-web-sdk)
EOF
)"
If gh is not available or the push fails, show the user the commands to run manually.
After the PR is open, show a summary of what's active and what can be added next.
Generate the checklist dynamically based on what was configured:
Your Faro setup is ready. Here's what's enabled:
✅ JavaScript error capture (automatic — errors, unhandled rejections, thrown exceptions) ✅ Core Web Vitals — LCP, FID/INP, CLS (automatic via
getWebInstrumentations()) ✅ Network request tracking — fetch and XHR timing and status codes [✅ Console log capture — OR — ❌ Console log capture disabled (captureConsole: false)] [✅ Distributed tracing — same-origin automatic; cross-origin if configured in advanced.md section A] [✅ Session tracking: persistent — OR — ✅ Session tracking: default (per-tab)] [✅ [N]% session sampling — OR — ✅ Session sampling: 100% (all sessions)] [✅ User identity — set on load OR set dynamically after login — OR — ☐ User identity: not configured] [✅ Navigation / route tracking — OR — ☐ Navigation tracking: not configured] [✅ React Error Boundary — OR — (omit for non-React)] [✅ Cookie consent gate (paused mode) — OR — (omit if not configured)] [✅ Microfrontend: host initializes Faro / child uses faro.api — OR — (omit if not applicable)]What you can add later:
☐ Custom events — instrument meaningful user actions:
faro.api?.pushEvent('feature_used', { feature: 'dark_mode' })☐ Custom measurements — track business metrics:
faro.api?.pushMeasurement({ type: 'cart_value', values: { total: 99.99 } })☐ Source map upload — resolve minified stack traces to real source lines in production. See: Faro source maps docs
☐ Alerting — create alerts on error rate spikes or Web Vitals degradation from the Grafana Cloud Frontend Observability panel.
☐ Backend correlation — link frontend traces to backend spans. Already enabled for same-origin requests. Add
propagateTraceHeaderCorsUrlsfor external APIs (advanced.md section A).