/analytics-events
Add product analytics events to track user interactions in the Metabase frontend
$ npx -y skills add metabase/metabase --skill analytics-events --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/analytics-events
Context preview
The summary Claude sees to decide when to auto-load this skill.
Add product analytics events to track user interactions in the Metabase frontend
SKILL.md
analytics-events.SKILL.mdname: analytics-events
description: Add product analytics events to track user interactions in the Metabase frontend
allowed-tools: Read, Write, Edit, Grep, Glob
Frontend Analytics Events Skill
This skill helps you add product analytics (Snowplow) events to track user interactions in the Metabase frontend codebase.
Quick Reference
Analytics events in Metabase use Snowplow with typed event schemas. Simple events are declared **where they are used** — `trackSimpleEvent` is generic and validates the payload at the call site.
**Key Files:**
- `frontend/src/metabase/analytics/event.ts` - Core tracking functions, `trackSimpleEvent` / `trackSchemaEvent` (import from `metabase/analytics`)
- `frontend/src/metabase-types/analytics/event.ts` - The shared `SimpleEventSchema` only. **Do not add event types here** (see below)
- `frontend/src/metabase-types/analytics/schema.ts` - Schema registry (custom/legacy schemas only)
- Feature-specific `analytics.ts` files - Where your tracking functions and any local types live
Quick Checklist
When adding a new analytics event:
- [ ] Pick an event name (snake_case, past tense)
- [ ] Add a tracking function to the feature's `analytics.ts` file, calling `trackSimpleEvent()`
- [ ] Keep any field unions (e.g. `"success" | "failure"`) as local types in that same file
- [ ] Import and call the tracking function at the interaction point
- [ ] Do **not** add an event type to `metabase-types/analytics/event.ts` or to any union
Event Schema Types
1. Simple Events (Most Common)
Use `SimpleEventSchema` for straightforward tracking. It supports these standard fields:
type SimpleEventSchema = {
event: string; // Required: Event name (snake_case)
target_id?: number | null; // Optional: ID of affected entity
triggered_from?: string | null; // Optional: UI location/context
duration_ms?: number | null; // Optional: Duration in milliseconds
result?: string | null; // Optional: Outcome (e.g., "success", "failure")
event_detail?: string | null; // Optional: Additional detail/variant
};**When to use:** 90% of events fit this schema. Use for clicks, opens, closes, creates, deletes, etc.
`trackSimpleEvent` is generic and enforces this schema on the object literal you pass it:
// frontend/src/metabase/analytics/event.ts
export function trackSimpleEvent<
T extends SimpleEventSchema &
Record<Exclude<keyof T, keyof SimpleEventSchema>, never>,
>(event: T) {
trackSchemaEvent("simple_event", event);
}That means a missing `event` or any field outside `SimpleEventSchema` is a compile error at the call site. There is no separate event type to declare and no `satisfies` clause to add — the old `ValidateEvent<...>` helper is no longer exported and is not part of the workflow.
`trackSchemaEvent` is generic too: it correlates the schema name with the payload type, so you can't send a dashboard event under the `simple_event` schema.
2. Custom Schemas (legacy, no events are being added)
Consider adding new event schema only in very special cases.
**Examples:** `DashboardEventSchema`, `CleanupEventSchema`, `QuestionEventSchema`
Step-by-Step: Adding a Simple Event
Example: Track when a user applies filters in a table picker
Step 1: Create Tracking Functions
In your feature's `analytics.ts` file (e.g., `enterprise/frontend/src/metabase-enterprise/data-studio/analytics.ts`):
import { trackSimpleEvent } from "metabase/analytics";
export const trackDataStudioTablePickerFiltersApplied = () => {
trackSimpleEvent({
event: "data_studio_table_picker_filters_applied",
});
};
export const trackDataStudioTablePickerFiltersCleared = () => {
trackSimpleEvent({
event: "data_studio_table_picker_filters_cleared",
});
};Step 2: Use in Components
Import and call the tracking function at the interaction point:
import {
trackDataStudioTablePickerFiltersApplied,
trackDataStudioTablePickerFiltersCleared,
} from "metabase-enterprise/data-studio/analytics";
function FilterPopover({ filters, onSubmit }) {
const handleReset = () => {
trackDataStudioTablePickerFiltersCleared(); // <- Track here
onSubmit(emptyFilters);
};
return (
<form
onSubmit={(event) => {
event.preventDefault();
trackDataStudioTablePickerFiltersApplied(); // <- Track here
onSubmit(form);
}}
>
{/* form content */}
</form>
);
}Using SimpleEventSchema Fields
All examples below live in the feature's own `analytics.ts` — nothing is registered centrally.
Example: Event with target_id
export const trackDataStudioLibraryCreated = (id: CollectionId) => {
trackSimpleEvent({
event: "data_studio_library_created",
target_id: Number(id),
});
};
// Usage
trackDataStudioLibraryCreated(newLibrary.id);Example: Event with triggered_from
// Local union, exported only if another feature needs to pass the same value
export type NewButtonLocation = "app-bar" | "empty-collection";
export const trackNewButtonClicked = (location: NewButtonLocation) => {
trackSimpleEvent({
event: "new_button_clicked",
triggered_from: location,
});
};
// Usage
<Button onClick={() => {
trackNewButtonClicked("app-bar");
handleCreate();
}}>
New
</Button>Example: Event with event_detail
Real example — `frontend/src/metabase/metadata/pages/shared/analytics.ts`:
export type MetadataEditEventDetail =
| "type_casting"
| "semantic_type_change"
| "visibility_change";
export const trackMetadataChange = (detail: MetadataEditEventDetail) => {
trackSimpleEvent({
event: "metadata_edited",
event_detail: detail,
triggered_from: "admin",
});
};
// Usage
trackMetadataChange("semantic_type_change");Example: Event with result and duration
See `frontend/src/metabase/archive/analyti
Read more
name: analytics-events description: Add product analytics events to track user interactions in the Metabase frontend allowed-tools: Read, Write, Edit, Grep, Glob
Frontend Analytics Events Skill
This skill helps you add product analytics (Snowplow) events to track user interactions in the Metabase frontend codebase.
Quick Reference
Analytics events in Metabase use Snowplow with typed event schemas. Simple events are declared **where they are used** — `trackSimpleEvent` is generic and validates the payload at the call site.
**Key Files:**
- `frontend/src/metabase/analytics/event.ts` - Core tracking functions, `trackSimpleEvent` / `trackSchemaEvent` (import from `metabase/analytics`)
- `frontend/src/metabase-types/analytics/event.ts` - The shared `SimpleEventSchema` only. **Do not add event types here** (see below)
- `frontend/src/metabase-types/analytics/schema.ts` - Schema registry (custom/legacy schemas only)
- Feature-specific `analytics.ts` files - Where your tracking functions and any local types live
Quick Checklist
When adding a new analytics event:
- [ ] Pick an event name (snake_case, past tense)
- [ ] Add a tracking function to the feature's `analytics.ts` file, calling `trackSimpleEvent()`
- [ ] Keep any field unions (e.g. `"success" | "failure"`) as local types in that same file
- [ ] Import and call the tracking function at the interaction point
- [ ] Do **not** add an event type to `metabase-types/analytics/event.ts` or to any union
Event Schema Types
1. Simple Events (Most Common)
Use `SimpleEventSchema` for straightforward tracking. It supports these standard fields:
type SimpleEventSchema = {
event: string; // Required: Event name (snake_case)
target_id?: number | null; // Optional: ID of affected entity
triggered_from?: string | null; // Optional: UI location/context
duration_ms?: number | null; // Optional: Duration in milliseconds
result?: string | null; // Optional: Outcome (e.g., "success", "failure")
event_detail?: string | null; // Optional: Additional detail/variant
};**When to use:** 90% of events fit this schema. Use for clicks, opens, closes, creates, deletes, etc.
`trackSimpleEvent` is generic and enforces this schema on the object literal you pass it:
// frontend/src/metabase/analytics/event.ts
export function trackSimpleEvent<
T extends SimpleEventSchema &
Record<Exclude<keyof T, keyof SimpleEventSchema>, never>,
>(event: T) {
trackSchemaEvent("simple_event", event);
}That means a missing `event` or any field outside `SimpleEventSchema` is a compile error at the call site. There is no separate event type to declare and no `satisfies` clause to add — the old `ValidateEvent<...>` helper is no longer exported and is not part of the workflow.
`trackSchemaEvent` is generic too: it correlates the schema name with the payload type, so you can't send a dashboard event under the `simple_event` schema.
2. Custom Schemas (legacy, no events are being added)
Consider adding new event schema only in very special cases.
**Examples:** `DashboardEventSchema`, `CleanupEventSchema`, `QuestionEventSchema`
Step-by-Step: Adding a Simple Event
Example: Track when a user applies filters in a table picker
Step 1: Create Tracking Functions
In your feature's `analytics.ts` file (e.g., `enterprise/frontend/src/metabase-enterprise/data-studio/analytics.ts`):
import { trackSimpleEvent } from "metabase/analytics";
export const trackDataStudioTablePickerFiltersApplied = () => {
trackSimpleEvent({
event: "data_studio_table_picker_filters_applied",
});
};
export const trackDataStudioTablePickerFiltersCleared = () => {
trackSimpleEvent({
event: "data_studio_table_picker_filters_cleared",
});
};Step 2: Use in Components
Import and call the tracking function at the interaction point:
import {
trackDataStudioTablePickerFiltersApplied,
trackDataStudioTablePickerFiltersCleared,
} from "metabase-enterprise/data-studio/analytics";
function FilterPopover({ filters, onSubmit }) {
const handleReset = () => {
trackDataStudioTablePickerFiltersCleared(); // <- Track here
onSubmit(emptyFilters);
};
return (
<form
onSubmit={(event) => {
event.preventDefault();
trackDataStudioTablePickerFiltersApplied(); // <- Track here
onSubmit(form);
}}
>
{/* form content */}
</form>
);
}Using SimpleEventSchema Fields
All examples below live in the feature's own `analytics.ts` — nothing is registered centrally.
Example: Event with target_id
export const trackDataStudioLibraryCreated = (id: CollectionId) => {
trackSimpleEvent({
event: "data_studio_library_created",
target_id: Number(id),
});
};
// Usage
trackDataStudioLibraryCreated(newLibrary.id);Example: Event with triggered_from
// Local union, exported only if another feature needs to pass the same value
export type NewButtonLocation = "app-bar" | "empty-collection";
export const trackNewButtonClicked = (location: NewButtonLocation) => {
trackSimpleEvent({
event: "new_button_clicked",
triggered_from: location,
});
};
// Usage
<Button onClick={() => {
trackNewButtonClicked("app-bar");
handleCreate();
}}>
New
</Button>Example: Event with event_detail
Real example — `frontend/src/metabase/metadata/pages/shared/analytics.ts`:
export type MetadataEditEventDetail =
| "type_casting"
| "semantic_type_change"
| "visibility_change";
export const trackMetadataChange = (detail: MetadataEditEventDetail) => {
trackSimpleEvent({
event: "metadata_edited",
event_detail: detail,
triggered_from: "admin",
});
};
// Usage
trackMetadataChange("semantic_type_change");Example: Event with result and duration
See `frontend/src/metabase/archive/analyti
Metabase is the easy, open-source way for everyone in your company to ask questions and learn from data.
Repo: metabase/metabase
Other skills on metabase.
- /add-malli-schemas
Efficiently add Malli schemas to API endpoints in the Metabase codebase with proper patterns, validation timing, and error handling
Open skill - /add-tracing
Add OpenTelemetry tracing spans to Clojure code following Metabase tracing conventions. Use when instrumenting backend code with trace coverage.
Open skill - /clojure-eval
Evaluate Clojure code via nREPL using clj-nrepl-eval. Use this when you need to test code, check if edited files compile, verify function behavior, or interact with a running REPL session.
Open skill - /clojure-review
Review Clojure and ClojureScript code changes for compliance with Metabase coding standards, style violations, and code quality issues. Use when reviewing pull requests or diffs containing Clojure/ClojureScript code.
Open skill - /clojure-write
Guide Clojure and ClojureScript development using REPL-driven workflow, coding conventions, and best practices. Use when writing, developing, or refactoring Clojure/ClojureScript code.
Open skill - /docs-review
Review documentation changes for compliance with the Metabase writing style guide. Use when reviewing pull requests, files, or diffs containing documentation markdown files.
Open skill

