configure-analytics
Skill7 starsChanged 16 days agoMITView on GitHub
Use when adding analytics to a frontend — one provider-agnostic seam (trackEvent/trackPageView), Core Web Vitals as real-user data, consent-gated by default (GDPR/TDDDG), path-only page views, no PII, no-op without config.
- Reads credentials
- Installs packages
2 other projects use a version of this.
Adopted by
Most recently taken up by
- VelimirMueller/claude-skillsword for word, first seen Oct 11, 2026
- IgorGanapolsky/ThumbGatetheir own version, first seen Mar 30, 2026
Other versions2 projects use a version of this.
Word for word · 1
- VelimirMueller/claude-skills★ 7100% same
Edited · 0
None found.
Wrote their own version · 1
- IgorGanapolsky/ThumbGate★ 30same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository156 other files it gives its agents.
- skills/swift/swiftui-design-principles/AGENTS.mdAGENTS.md
- skills/swift/ios-code-audit/CLAUDE.mdCLAUDE.md
- skills/swift/swift-focusengine-pro/llms.txtllms.txt
- build-llm-seamSkill
- build-mcp-serverSkill
- build-rag-pipelineSkill
- secure-llm-featuresSkill
- set-up-llm-evalsSkill
- build-nextjs-backendSkill
- build-supabase-edge-functionSkill
- configure-backend-testsSkill
- design-http-apiSkill
- harden-backendSkill
- scaffold-fastapi-serviceSkill
- scaffold-go-serviceSkill
- scaffold-hono-serviceSkill
- secure-supabase-rlsSkill
- set-up-backend-authSkill
- set-up-background-jobsSkill
- set-up-databaseSkill
- set-up-nextjs-supabase-authSkill
- set-up-observabilitySkill
- set-up-supabaseSkill
- build-cliSkill
- release-cliSkill
- set-up-dev-toolchainSkill
- audit-securitySkill
- audit-toolchainSkill
- extend-skillsetSkill
- promote-releaseSkill
- set-up-stack-profileSkill
- write-commit-messagesSkill
- write-pull-requestsSkill
- analyze-complexitySkill
- apply-algorithmsSkill
- apply-design-patternsSkill
- apply-design-principlesSkill
- apply-solidSkill
- choose-data-structureSkill
- debug-systematicallySkill
- enforce-dependency-ruleSkill
- refactor-safelySkill
- slice-and-estimateSkill
- write-clean-testsSkill
- write-readable-codeSkill
- clean-frontend-scaffoldingSkill
- configure-accessibilitySkill
- configure-ciSkill
- configure-error-trackingSkill
- configure-lintingSkill
- configure-test-stackSkill
- configure-typescriptSkill
- create-moduleSkill
- optimize-performanceSkill
- scaffold-frontend-projectSkill
- set-up-authSkill
- set-up-design-systemSkill
- set-up-document-headSkill
- set-up-error-boundariesSkill
- set-up-feature-flagsSkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
3 rules. About 0 are common, 1 is distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 16
Read frontend.framework (router hook in step 7) and package_manager (commands use pnpm).
- Distinctive: line 19
No analytics → full setup (seam + vitals + page views).
- Very specific: line 26
Avoid cookie-based GA unless a consent banner already exists.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Configure Analytics
1. Audit current state
cat .claude/stack-profile.md 2>/dev/null || cat ~/.claude/stack-profile.md 2>/dev/null # frontend.framework, frontend.meta, observability, package_manager
grep -E '"(@plausible-analytics/tracker|plausible-tracker|posthog-js|@vercel/analytics|web-vitals)"' package.json 2>/dev/null
ls src/libs/analytics.ts 2>/dev/nullRead frontend.framework (router hook in step 7) and package_manager (commands use pnpm); observability.backend is for errors and traces, not product analytics — keep them separate. Prerequisites: optimize-performance (we forward its web-vitals) and set-up-routing (page views fire on route change).
2. Decide what to do
- No analytics → full setup (seam + vitals + page views).
- Analytics calls scattered in components → centralize behind the seam (step 5).
3. Choose a provider, and decide consent first
- Consent decides the code, not the provider. The seam sends nothing until the user opts in (
CONSENT_REQUIRED = true). This is the default for any EU/German audience: GDPR needs a legal basis and transparency, and § 25 TDDDG requires consent for any read or write on the user's device. A cookieless tool can fall outside § 25 and still needs a documented basis; the npm Plausible tracker, for one, readslocalStorage.plausible_ignore. SetCONSENT_REQUIRED = falseonly with a written legal/DPO decision for that product. - Cookieless (Plausible, Fathom): no cookies, no personal profile, tiny script. Default provider.
- Product analytics (PostHog): funnels and session replay are richer and need consent plus a data-processing agreement; review its privacy settings.
- Avoid cookie-based GA unless a consent banner already exists.
Why:
analytics-patterns.md.
4. Install
pnpm add @plausible-analytics/tracker # or posthog-js, @vercel/analytics — the seam hides the choice(plausible-tracker, the older package, was last released in 2022 and its API differs; use the @plausible-analytics/tracker package.)
5. The analytics seam (provider-agnostic, no-op without config or consent)
One module owns analytics — swapping providers is a one-file change, exactly like captureError. Checked against @plausible-analytics/tracker 0.4 (init runs once; track(name, { props }) takes string props).
// src/libs/analytics.ts
import { init, track } from '@plausible-analytics/tracker';
type Props = Record<string, string | number | boolean>;
declare global {
interface Navigator {
globalPrivacyControl?: boolean; // not in lib.dom yet
}
}
/** true = nothing is sent before an explicit opt-in. Flip only with a written legal/DPO decision. */
const CONSENT_REQUIRED = true;
const CONSENT_KEY = 'analytics-consent';
let started = false;
function consentGranted(): boolean {
try {
return localStorage.getItem(CONSENT_KEY) === 'granted';
} catch {
return false; // storage blocked: treat as no consent
}
}
/** Evaluated on every request, so withdrawing consent stops sending at once. */
function mayTrack(): boolean {
if (navigator.globalPrivacyControl || navigator.doNotTrack === '1') return false;
return !CONSENT_REQUIRED || consentGranted();
}
export function initAnalytics(): void {
const domain = import.meta.env.VITE_ANALYTICS_DOMAIN;
if (!domain || started || !mayTrack()) return; // no config, already running, or no consent: no-op
init({
domain,
autoCapturePageviews: false, // page views come from the router (step 7), the same for any provider
transformRequest: (payload) => (mayTrack() ? payload : null),
});
started = true;
}
export function trackEvent(name: string, props?: Props): void {
if (!started || !mayTrack()) return; // mayTrack here too: after withdrawal the seam stops preparing payloads, not just the request
// Plausible custom properties are strings.
const stringProps = props && Object.fromEntries(Object.entries(props).map(([k, v]) => [k, String(v)]));
track(name, stringProps ? { props: stringProps } : {});
}
export function trackPageView(path: string): void {
// Path only: query strings carry tokens, emails and search terms. The full URL keeps the origin — in a
// local/preview run that is localhost:PORT, so do not set VITE_ANALYTICS_DOMAIN with consent granted there.
if (started && mayTrack()) track('pageview', { url: `${location.origin}${path.split(/[?#]/)[0]}` });
}
/** Call from the consent banner (the banner itself is outside this skill). */
export function setConsent(granted: boolean): void {
try {
localStorage.setItem(CONSENT_KEY, granted ? 'granted' : 'denied');
} catch {
/* storage blocked: the choice lasts for this page view only */
}
if (granted) {
const wasStarted = started;
initAnalytics();
// The router hook (step 7) fires on the first load too; only fire here when consent is what started the seam.
if (!wasStarted) trackPageView(location.pathname);
}
}Call initAnalytics() once from the app entry. Gate any other third-party script the same way (Sentry replay, chat widgets): call it from setConsent, never at load. Add VITE_ANALYTICS_DOMAIN and the tracker's endpoint to the CSP connect-src (set-up-security-headers); proxy the endpoint through your own domain if ad blockers matter.
6. Wire Core Web Vitals as real-user monitoring
Feed the reportWebVitals from optimize-performance into the seam:
// app entry, prod only
import { reportWebVitals } from '@/libs/reportWebVitals';
import { trackEvent } from '@/libs/analytics';
if (import.meta.env.PROD) {
reportWebVitals(({ name, value }) =>
trackEvent('web-vital', { metric: name, value: Math.round(name === 'CLS' ? value * 1000 : value) }), // CLS ×1000: rounding a 0.0x score to an integer would erase it
);
}Now LCP/INP/CLS are measured on real users, not just your Lighthouse run. They go through the same seam, so they obey the same consent gate: no consent, no field data. (CLS reports only from Chromium.)
7. Page views on route change
// React (TanStack Router) — fires on the first load too
router.subscribe('onResolved', ({ toLocation }) => trackPageView(toLocation.pathname));
// Vue Router
router.afterEach((to) => trackPageView(to.path));The seam passes the path only, so the router hook never leaks a query string. (A tracker's own SPA auto-capture would double count; step 5 turns it off.)
8. Track meaningful events, not everything
Define a small taxonomy (signup_completed, todo_created, checkout_started) and call trackEvent at those moments — typically inside the mutation onSuccess. Never put PII (email, name, IDs that identify a person) in props.
9. Verify
pnpm build && pnpm previewWith VITE_ANALYTICS_DOMAIN set and consent granted: navigating fires page views, a web-vital event arrives, and a sample trackEvent shows in the provider dashboard. Network tab before consent: no request to the provider. After setConsent(false): no further requests. Without the env var: nothing loads, app unaffected.
References
- ./analytics-patterns.md — the seam, consent (GDPR/TDDDG), no-PII, web-vitals RUM, event taxonomy, no-op-without-config.
- ../optimize-performance/SKILL.md — the
reportWebVitalssource. - ../configure-error-tracking/SKILL.md — the sibling seam pattern.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

