
Analytics
- 11 installs
- 44.5k repo stars
- Updated August 5, 2026
- getsentry/sentry
analytics skill documents Instrument and discover analytics events in Sentry's frontend UI.
About
analytics skill documents Instrument and discover analytics events in Sentry's frontend UI. Use when adding tracking to buttons, pages, modals, or custom interactions, when defining new analytics events, when searching for existing events, when auditing analytics coverage for a feature, or when answering questions about how . name: analytics description: Instrument and discover analytics events in Sentry's frontend UI. Use when adding tracking to buttons, pages, modals, or custom interactions, when defining new analytics events, when searching for existing events, when auditing analytics coverage for a feature, or when answering questions about how users interact with a feature. Trigger on "add analytics", "track event
- Instrument and discover analytics events in Sentry's frontend UI.
- Platform-specific setup patterns for analytics.
- Evidence-backed steps from upstream SKILL.md.
- When-to-use criteria for analytics versus alternatives.
Analytics by the numbers
- 11 all-time installs (skills.sh)
- Ranked #807 of 1,352 Code Review & Quality skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
analytics capabilities & compatibility
- Capabilities
- analytics quick start · analytics when to use guidance · analytics integration patterns
- Works with
- sentry
- Use cases
- code review
What analytics says it does
Add analytics events to Sentry's frontend UI using established patterns.
When the user asks about usage, adoption, or interaction counts for a feature:
npx skills add https://github.com/getsentry/sentry --skill analyticsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 11 |
|---|---|
| repo stars | ★ 44.5k |
| Last updated | August 5, 2026 |
| Repository | getsentry/sentry ↗ |
How do I use analytics correctly?
Instrument and discover analytics events in Sentry's frontend UI. Use when adding tracking to buttons, pages, modals, or custom interactions, when defining new analytics events, when searching for exi
Who is it for?
Teams implementing analytics workflows from the catalog.
Skip if: Skip when requirements clearly match a different specialized stack.
When should I use this skill?
User asks about analytics, instrument and discover analytics events in sentry's frontend ui. use when adding tracking.
What you get
Working analytics setup with validated configuration and next steps.
Files
Analytics Instrumentation
Add analytics events to Sentry's frontend UI using established patterns.
Answering "How Many People Do X?"
When the user asks about usage, adoption, or interaction counts for a feature:
1. Find the event: search Amplitude first (fastest), fall back to grepping the codebase. 2. If the Amplitude MCP is connected, query the data directly and report results. 3. If no matching event exists, tell the user the event is not tracked — then use AskUserQuestion to ask whether they want to instrument it. Do not proceed to instrumentation without explicit confirmation.
Read references/amplitude-mcp.md for the full discovery and querying workflow.
Before Any Change: Search First
NEVER create a new event without checking if one already exists.
1. Search static/app/utils/analytics/ for events matching the feature domain. 2. Grep for keywords related to the interaction (e.g., clicked, viewed, created). 3. If a matching event exists, reuse it — add parameters if needed rather than creating a duplicate.
grep -rn "keyword" static/app/utils/analytics/ --include="*.tsx"Event Naming Rules
| Rule | Example |
|---|---|
Use snake_case with dots as separators | feedback.list-item-selected |
| First segment = feature domain | dashboards2., issue_details., feedback. |
| Middle segments = section/context (optional) | dashboards2.edit. |
| Last segment = action | .clicked, .viewed, .created, .changed |
| Match the existing domain file's prefix | If events are in feedbackAnalyticsEvents.tsx, use feedback. prefix |
Standard action suffixes:
| User action | Suffix |
|---|---|
| Clicks a button/link | .clicked or _clicked |
| Views a page | .viewed |
| Submits a form | .submitted or .created |
| Changes a setting | .changed |
| Renders/loads content | .rendered or .loaded |
| Dismisses UI | .dismissed |
| Opens a modal/panel | .opened |
Choose the Right Tracking Pattern
| What to track | Pattern | Open reference |
|---|---|---|
| Page view on route navigation | Route analytics hooks | references/tracking-patterns.md § Route-Level |
| Button or link click | Button analyticsEventKey prop | references/tracking-patterns.md § Button |
| Custom interaction (toggle, drag, select) | trackAnalytics() call | references/tracking-patterns.md § Manual |
| Modal or panel open/close | trackAnalytics() in handler | references/tracking-patterns.md § Manual |
| UI area context for events | AnalyticsArea wrapper | references/tracking-patterns.md § Area Context |
When You Need to Define a New Event
Read references/event-definitions.md for step-by-step instructions.
Common Mistakes and Debugging
Read references/troubleshooting.md when:
- An event isn't firing or appearing in Amplitude
- You see TypeScript errors when calling
trackAnalytics - You need to debug analytics locally
- You're unsure whether an event needs an Amplitude name
Key Files
| File | Purpose |
|---|---|
static/app/utils/analytics.tsx | Master registry — all event maps merged, trackAnalytics export |
static/app/utils/analytics/{domain}AnalyticsEvents.tsx | Domain-specific event type definitions and name maps |
static/app/utils/analytics/makeAnalyticsFunction.tsx | Factory that creates typed trackAnalytics — do not call directly |
static/app/utils/routeAnalytics/useRouteAnalyticsEventNames.tsx | Hook for route-level page view event names |
static/app/utils/routeAnalytics/useRouteAnalyticsParams.tsx | Hook for route-level page view parameters |
static/app/components/analyticsArea.tsx | AnalyticsArea component and useAnalyticsArea hook |
static/app/components/core/button/types.tsx | Button analytics props (analyticsEventKey, analyticsEventName, analyticsParams) |
Interaction Rules
Users of this skill may be less technical. Use AskUserQuestion at every decision point instead of dumping plans or code.
| Situation | Action |
|---|---|
| Event not found, user asked a data question | Use AskUserQuestion: "This isn't tracked yet. Want me to add instrumentation?" |
| User confirms they want instrumentation | Go straight to implementation. Do not show code previews or step-by-step plans — just make the changes and summarize what you did. |
| Implementation is done, needs user action (e.g., Reload registration) | State the remaining step clearly in your summary. |
Never dump code blocks as a "plan" and then ask "Want me to make these changes?" — either present a short plain-English summary via AskUserQuestion for confirmation, or proceed directly if the user already asked for instrumentation.
Event Pipeline
Every trackAnalytics call flows through the GetSentry override in static/gsApp/utils/rawTrackAnalyticsEvent.tsx:
| Destination | When it fires | What it uses | How to query |
|---|---|---|---|
| Reload | Always | eventKey | Redash |
| Amplitude | When eventName is non-null and org exists | eventName | Amplitude UI or MCP |
| Pendo | Same as Amplitude | eventName | Pendo |
- Set
eventNameto a string (e.g.,'Logs Trace Link Clicked') to send to both Reload and Amplitude. This is the default for almost all events. - Set
eventNametonullonly for high-volume events that would be too expensive for Amplitude. These are Reload-only and queryable via Redash. - Reload accepts events with
allow_no_schema: true— no separate registration step is needed. - When searching for events, note that Reload-only events (
nullname) will not appear in Amplitude search. Fall back to grepping the codebase if Amplitude returns no results.
Non-Negotiable Constraints
1. All events must be type-safe. Every event key must exist in a *EventParameters type and be registered in the domain's event map. 2. All events flow through `trackAnalytics()`. Never call window.analytics, Amplitude.track(), or any other analytics SDK directly. 3. Organization context is automatic. Pass organization to trackAnalytics — the override system handles the rest. 4. Reuse over create. Always search for existing events before defining new ones. 5. One event per interaction. Do not fire multiple events for the same user action. 6. No PII in event parameters. Never include user emails, IP addresses, full names, or other personally identifiable information. Use opaque IDs (org ID, user ID) when identity context is needed.
Amplitude MCP — Discovery and Querying
Setup (One-Time)
If the Amplitude MCP is not connected:
1. Tell the user to run /mcp in Claude Code 2. Select "claude.ai Amplitude" from the list 3. Authenticate via Sentry SSO
Once connected, the mcp__claude_ai_Amplitude__* tools become available.
Discover the Amplitude Project
Call get_context to find the Sentry organization and its projects. Use the appId for the sentry.io project as the projectId in all subsequent Amplitude tool calls.
Discovery Workflow
When a user asks "how many people do X?" or "are people using Y?":
Step 1: Find the Event
Use search to find matching events by keyword:
mcp__claude_ai_Amplitude__search({
queries: ["seer", "autofix"],
entityTypes: ["EVENT"],
limitPerQuery: 20,
search_goal: "Find events related to the seer feature on issue details"
})This returns Amplitude event names (the eventName from our event maps, e.g., "Issue Details: Seer Opened").
If search returns nothing, fall back to grepping the codebase:
grep -rn "seer\|autofix" static/app/utils/analytics/ --include="*.tsx"Step 2: Get Event Properties (Optional)
If the user needs to filter or break down by a property:
mcp__claude_ai_Amplitude__get_properties({
propertyType: "event",
projectId: "<projectId>",
eventType: "Issue Details: Seer Opened"
})Step 3: Query the Data
Use query_dataset for ad-hoc queries:
mcp__claude_ai_Amplitude__query_dataset({
projectId: "<projectId>",
definition: {
type: "eventsSegmentation",
app: "<projectId>",
name: "Seer Opens Last 30 Days",
params: {
range: "Last 30 Days",
events: [{
event_type: "Issue Details: Seer Opened",
filters: [],
group_by: []
}],
metric: "uniques",
countGroup: "User",
groupBy: [],
interval: 1,
segments: [{ conditions: [] }]
}
}
})Use query_chart if the user provides an existing chart ID or URL.
Step 4: Report Results
- State the event name used and the date range queried.
- Report unique users (not total event count) unless the user asks for totals.
- Offer to break down by property (platform, org, etc.) if the numbers need context.
Common Query Patterns
| User question | Metric | Event type pattern |
|---|---|---|
| "How many people view X page?" | uniques | "Page View: ..." |
| "How many clicks on X button?" | totals | "Feature: Button Clicked" |
| "What's the funnel from X to Y?" | funnel | Use type: "funnels" with ordered events |
| "Are people coming back to X?" | retention | Use type: "retention" |
Searching for Dashboards and Charts
If the user wants an existing dashboard rather than raw data:
mcp__claude_ai_Amplitude__search({
queries: ["seer dashboard", "autofix metrics"],
entityTypes: ["DASHBOARD", "CHART"],
limitPerQuery: 10
})When the MCP Is Not Connected
Fall back to the codebase:
1. Grep event files for the Amplitude name (eventName in the event map). 2. Report the event key and Amplitude name so the user can search Amplitude manually. 3. Suggest they connect the Amplitude MCP for direct querying: run /mcp → select "claude.ai Amplitude".
Defining New Analytics Events
Step 1: Find or Create the Domain Event File
Event files live in static/app/utils/analytics/ and follow the naming pattern {domain}AnalyticsEvents.tsx.
Discover existing domain files:
ls static/app/utils/analytics/*AnalyticsEvents.tsxPrefer adding events to an existing domain file. Create a new file only when the feature has no natural home in an existing domain.
Step 2: Add the Event Type
Add the event key and its parameter types to the domain's *EventParameters type:
export type FeedbackEventParameters = {
// Existing events...
'feedback.filter-applied': {
filter_type: string;
source: 'list' | 'detail';
};
};Parameter typing rules:
| Rule | Example |
|---|---|
Use specific string literals over string when values are known | `source: 'list' \ |
Use Record<string, unknown> for events with no custom params | 'feedback.item-rendered': Record<string, unknown> |
Never use any for parameter types | Use unknown or specific types |
Include organization only if you need to override automatic org context | Rarely needed |
Step 3: Add the Event Map Entry
Add the event key → Amplitude display name mapping:
export const feedbackEventMap: Record<keyof FeedbackEventParameters, string | null> = {
// Existing entries...
'feedback.filter-applied': 'Feedback: Filter Applied',
};Amplitude name rules:
| Scenario | Value |
|---|---|
| Event should go to Amplitude | 'Human Readable: Title Case Name' |
| Event is Reload-only (internal metrics) | null |
The Amplitude name follows 'Domain: Action Description' format in Title Case.
Step 4: Register in the Master Registry
If you created a new domain file, register it in static/app/utils/analytics.tsx:
1. Import the type and event map:
import type {MyDomainEventParameters} from './analytics/myDomainAnalyticsEvents';
import {myDomainEventMap} from './analytics/myDomainAnalyticsEvents';2. Add the type to the EventParameters interface:
interface EventParameters
// ...existing types
extends MyDomainEventParameters, Record<string, Record<string, any>> {}3. Spread the map into allEventMap:
const allEventMap: Record<string, string | null> = {
// ...existing maps
...myDomainEventMap,
};If you added events to an existing domain file, skip this step — the domain is already registered.
Complete Example: New Event in Existing Domain
Adding a "filter applied" event to the feedback domain:
// In static/app/utils/analytics/feedbackAnalyticsEvents.tsx
export type FeedbackEventParameters = {
// ... existing events
'feedback.filter-applied': {
filter_type: string;
source: 'list' | 'detail';
};
};
export const feedbackEventMap: Record<keyof FeedbackEventParameters, string | null> = {
// ... existing entries
'feedback.filter-applied': 'Feedback: Filter Applied',
};Anti-Pattern: Untyped Event
// BAD — will cause TypeScript error, event key not registered
trackAnalytics('feedback.my-new-thing', {
organization,
some_param: 'value',
});
// GOOD — define the event type and map entry first, then call
trackAnalytics('feedback.filter-applied', {
organization,
filter_type: 'status',
source: 'list',
});Tracking Patterns
Route-Level Page Views
Use route analytics hooks for tracking when a user visits a page. These fire automatically on route navigation.
Where: Inside the top-level component for a route (the component rendered by React Router).
import {useRouteAnalyticsEventNames} from 'sentry/utils/routeAnalytics/useRouteAnalyticsEventNames';
import {useRouteAnalyticsParams} from 'sentry/utils/routeAnalytics/useRouteAnalyticsParams';
function MyFeaturePage() {
const organization = useOrganization();
// Register the page view event
useRouteAnalyticsEventNames('my_feature.viewed', 'My Feature: Viewed');
// Attach contextual parameters
useRouteAnalyticsParams({
has_data: true,
tab: 'overview',
});
return <div>...</div>;
}Rules:
- Call
useRouteAnalyticsEventNamesexactly once per route component. - Call
useRouteAnalyticsParamsto add context. It can be called multiple times — params merge. useRouteAnalyticsParamsmust be called within 2 seconds of organization context loading.- Subcomponents can call
useRouteAnalyticsParamsto add their own params (e.g., trace status, replay availability). - The event fires automatically — do not also call
trackAnalyticsfor the same page view.
Real-world example (from static/app/views/issueDetails/groupDetails.tsx):
useRouteAnalyticsEventNames('issue_details.viewed', 'Issue Details: Viewed');
useRouteAnalyticsParams({
...getAnalyticsDataForGroup(group),
...getAnalyticsDataForEvent(event),
...getAnalyicsDataForProject(project),
tab,
group_event_type: groupEventType,
});Button Click Tracking
Use built-in button analytics props for simple click tracking. No event definition required for Reload-only tracking.
<Button
analyticsEventKey="feedback.filter-applied"
analyticsEventName="Feedback: Filter Applied"
analyticsParams={{filter_type: 'status', source: 'sidebar'}}
>
Apply Filter
</Button>Props:
| Prop | Required | Purpose |
|---|---|---|
analyticsEventKey | Yes | Reload event key (snake_case with dots) |
analyticsEventName | No | Amplitude display name. Omit to skip Amplitude. |
analyticsParams | No | Additional key-value pairs sent with the event |
Rules:
- Prefer button props over a manual
trackAnalyticscall in anonClickhandler. - If you need the event to be type-safe and appear in the typed event registry, also define it in the domain event file. Button props work without registration but lose type safety.
- The tracking fires via
TrackingContext— the GetSentry override wires it to Reload/Amplitude.
Manual trackAnalytics() Calls
Use for interactions that aren't button clicks or page views: toggles, drag actions, form submissions, modal opens, etc.
import {trackAnalytics} from 'sentry/utils/analytics';
function handleFilterChange(filterType: string) {
trackAnalytics('feedback.filter-applied', {
organization,
filter_type: filterType,
source: 'list',
});
// ... actual handler logic
}Rules:
- Always pass
organization(string slug or Organization object). - The event key must be defined in a
*EventParameterstype and registered in the domain event map. - Call
trackAnalyticsat the point of user action, not in render or effects (unless tracking a "viewed" event). - For "viewed" events in components that aren't route-level, use a
useEffect:
useEffect(() => {
trackAnalytics('feedback.banner-viewed', {
organization,
});
}, [organization]);Area Context
Use AnalyticsArea to tag events with their UI location. This is useful when the same component appears in multiple contexts.
import {AnalyticsArea, useAnalyticsArea} from 'sentry/components/analyticsArea';
// Wrap a section of UI
<AnalyticsArea name="feedback">
<AnalyticsArea name="details">
<MyComponent /> {/* useAnalyticsArea() returns "feedback.details" */}
</AnalyticsArea>
</AnalyticsArea>;
// Use in a component
function MyComponent() {
const area = useAnalyticsArea();
function handleClick() {
trackAnalytics('feedback.action-clicked', {
organization,
area, // "feedback.details"
});
}
}Rules:
- Areas nest with dot notation:
outer.inner. - Use
overrideParentto strip the outer area (e.g., for modals that should have their own top-level area). - Do not branch app logic on the area value — it is for analytics metadata only.
Choosing Between Patterns
| Situation | Use |
|---|---|
| User navigates to a page | Route analytics hooks |
User clicks a <Button> | Button analytics props |
| User clicks a non-Button element | trackAnalytics() in click handler |
| User submits a form | trackAnalytics() in submit handler |
| Component renders / becomes visible | trackAnalytics() in useEffect |
| Same component used in multiple places | AnalyticsArea + useAnalyticsArea() |
| Feature flag should be tracked | Add flag value as a param to the existing event |
Troubleshooting
Common Mistakes
| Symptom | Cause | Fix |
|---|---|---|
| TypeScript error: event key not found | Event key not defined in *EventParameters type | Add the event to the domain's type definition and event map |
| Event fires in Reload but not Amplitude | eventName is null in the event map | Set eventName to a human-readable string if Amplitude tracking is needed |
| Duplicate events on page view | Both route analytics hook AND manual trackAnalytics used | Remove the manual call — route analytics fires automatically |
| Event params missing organization | organization not passed to trackAnalytics | Always pass organization as a parameter |
| Button click not tracked | Missing analyticsEventKey prop | Add analyticsEventKey to the Button component |
| Area context returns empty string | Component not wrapped in AnalyticsArea | Wrap parent component with <AnalyticsArea name="..."> |
| Route analytics params stale | Params set after 2s timeout | Call useRouteAnalyticsParams earlier in the render cycle |
Debugging Locally
Enable analytics debug logging in your browser console:
localStorage.setItem('DEBUG_ANALYTICS', '1');This logs all trackAnalytics calls and button tracking events to the console. Remove with:
localStorage.removeItem('DEBUG_ANALYTICS');Anti-Patterns
Direct SDK calls
// NEVER do this
window.analytics.track('my_event', {...});
Amplitude.track('My Event', {...});
// ALWAYS use trackAnalytics
trackAnalytics('my_feature.event', {organization, ...});Untyped events
// NEVER call trackAnalytics with an unregistered key
// TypeScript will catch this, but if you bypass it with `as any`:
trackAnalytics('nonexistent.event' as any, {organization});
// ALWAYS define the event type first, then call trackAnalyticsTracking in render
// NEVER track in the render body — fires on every re-render
function MyComponent() {
trackAnalytics('my_feature.viewed', {organization}); // BAD
return <div />;
}
// ALWAYS use useEffect for "viewed" events
function MyComponent() {
useEffect(() => {
trackAnalytics('my_feature.viewed', {organization});
}, [organization]);
return <div />;
}Creating duplicate events
// NEVER create a new event when one already exists
// Search first: grep -rn "feedback" static/app/utils/analytics/
// If 'feedback.list-item-selected' exists, don't create 'feedback.list_item_clicked'
// Reuse the existing event and add params if neededOverly broad parameter types
// AVOID — loses type safety
'my_feature.action': {
type: string; // What values can this be?
data: any; // Completely untyped
};
// PREFER — explicit and self-documenting
'my_feature.action': {
type: 'create' | 'update' | 'delete';
item_count: number;
};Analytics Instrumentation Specification
Intent
Guide users and agents through adding analytics events to Sentry's frontend UI using established patterns. Prioritizes reusing existing events, enforcing type safety, and following naming conventions. Designed to be safe for less-technical users (design, PM, sales) who may not know the analytics architecture.
Scope
In scope:
- Frontend analytics event definition (TypeScript types, event maps)
- Frontend tracking instrumentation (
trackAnalytics, route hooks, button props,AnalyticsArea) - Event naming conventions and parameter typing
- Searching for and reusing existing events
- Local debugging with
DEBUG_ANALYTICS
Out of scope:
- Backend analytics event creation (
src/sentry/analytics/) - Amplitude dashboard configuration
- Google Analytics configuration
- Performance metrics (
metric.mark,metric.measure)
Users And Trigger Context
- Common user requests: "add analytics to this button", "track when users view this page", "what events exist for dashboards", "I need to track clicks on the new filter"
- Should not trigger for: backend-only analytics, Sentry SDK instrumentation, performance monitoring, error tracking setup
Runtime Contract
- Required first actions: search existing events before defining new ones
- Required outputs: code changes to event definition files and instrumentation call sites
- Non-negotiable constraints: all events must be type-safe; all events flow through
trackAnalytics; reuse existing events when possible - Expected bundled files loaded at runtime:
references/event-definitions.md,references/tracking-patterns.md,references/troubleshooting.md,references/amplitude-mcp.md
Source And Evidence Model
Authoritative sources:
static/app/utils/analytics.tsx— master event registrystatic/app/utils/analytics/*.tsx— domain event definitionsstatic/app/utils/analytics/makeAnalyticsFunction.tsx— factory functionstatic/app/utils/routeAnalytics/— route-level tracking hooksstatic/app/components/analyticsArea.tsx— area contextstatic/app/components/core/button/types.tsx— button analytics props
Data that must not be stored:
- Customer organization slugs, names, or IDs
- Internal Amplitude project keys
- Reload backend credentials
Reference Architecture
SKILL.mdcontains: routing table, naming rules, non-negotiable constraints, key filesreferences/event-definitions.mdcontains: step-by-step event creation, registration, examplesreferences/tracking-patterns.mdcontains: route-level, button, manual, and area tracking patternsreferences/troubleshooting.mdcontains: common mistakes, debugging, anti-patternsreferences/amplitude-mcp.mdcontains: Amplitude MCP setup, event discovery, ad-hoc querying, fallback workflow
Validation
- Lightweight validation: TypeScript compilation catches unregistered event keys
- Deeper validation:
DEBUG_ANALYTICS=1in browser console confirms events fire - Acceptance gates: event key exists in domain type, event map entry exists,
trackAnalyticscall compiles
Known Limitations
- Reload backend registration is in a separate repo (
getsentry/reload) — this skill cannot automate that step - Button analytics props are not type-checked against the event registry
- Route analytics timing constraint (2s) is not enforced at compile time
Maintenance Notes
- When to update
SKILL.md: new tracking patterns added to the codebase, analytics architecture changes - When to update references: existing patterns deprecated or new tracking patterns introduced
Related skills
FAQ
What does analytics do?
analytics skill documents Instrument and discover analytics events in Sentry's frontend UI.
When should I use analytics?
User asks about analytics, instrument and discover analytics events in sentry's frontend ui. use when adding tracking.
Is this skill safe to install?
Review the Security Audits panel on this page before installing in production.