analytics-tracking
Skill104 starsChanged 3 days agoApache-2.0View on GitHub
How to track analytics events in tangle-ui and naming conventions for action_type values. Use when adding a new tracked event, naming an action_type, or working with the analytics provider.
We split this skill into the rules it gives an agent
7 rules. 0 are common, 1 is distinctive, and 6 are very specific to this project.
0
Common
Most of the 55 edited versions we read keep these.
1
Distinctive
This author's own choices. Worth a look.
6
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 40
When a child component renders the interactive element but the parent owns the tracking context, prefer prop drilling or forwarding rest …
- Distinctive: line 78
When metadata depends on component state that is known at render time, compute it inline.
- Very specific: line 91
Use useAnalytics and call track() directly only when the document-level click listener cannot handle the scenario:
- Very specific: line 136
Use dot-separated, snake_case segments:
- Very specific: line 165
All segments are snake_case — no camelCase, no hyphens.
- Very specific: line 168
Keep hierarchy shallow — prefer pipeline.component over pipeline.canvas.node.component.
- Very specific: line 169
Do not embed counts, IDs, or dynamic values in the action_type string.
“Common” is measured: kept by at least 6 in 10 of the 55 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Analytics Tracking
Tracking click events (preferred approach)
For click events on interactive elements (<button>, <a>, <summary>, or elements with role="button" / role="link"), use the tracking() helper to attach data attributes. A document-level click listener (useClickTracking) automatically fires the analytics event — no manual track() call needed.
import { tracking } from "@/utils/tracking";
<Button onClick={handleSave} {...tracking("pipeline_editor.save_pipeline")}>
Save
</Button>;The listener appends .click to the identifier automatically, so "pipeline_editor.save_pipeline" fires as "pipeline_editor.save_pipeline.click".
With metadata
Pass a second argument for event-specific properties. The metadata is serialized as a data-tracking-metadata JSON attribute on the DOM element.
<Button
onClick={() => handleLayout(algo)}
{...tracking("pipeline_canvas.tool_bar.auto_layout_select", {
selected_layout: algo,
page_type: "pipeline_editor",
})}
>
{algo}
</Button>Prop drilling for click tracking
When a child component renders the interactive element but the parent owns the tracking context, prefer prop drilling or forwarding rest props so data-tracking-id reaches the DOM element. This is intentional — it keeps tracking declarative and avoids manual track() calls scattered across the codebase.
The parent passes the tracking attributes, and ActionButton forwards its rest props down the
<TooltipButton> → <Button> → DOM chain:
<ActionButton
tooltip="Export Pipeline"
icon="FileDown"
onClick={handleExport}
{...tracking("pipeline_editor.pipeline_actions.export_pipeline")}
/>;
export const ActionButton = ({
tooltip,
onClick,
icon,
...rest
}: ActionButtonProps) => (
<TooltipButton onClick={onClick} tooltip={tooltip} {...rest}>
<Icon name={icon} />
</TooltipButton>
);For components that wrap interactive elements (e.g. Radix asChild patterns), spread tracking() on the wrapper — Radix merges props onto the child:
<DialogTrigger
asChild
{...tracking("pipeline_editor.task_node.component_info")}
>
<InfoIconButton />
</DialogTrigger>Dynamic metadata at render time
When metadata depends on component state that is known at render time, compute it inline. The data attribute updates on each render:
<Button
onClick={() => toggleFavorite()}
{...(analyticsActionType
? tracking(analyticsActionType, { new_value: !active })
: {})}
>When to use manual track() instead
Use useAnalytics and call track() directly only when the document-level click listener cannot handle the scenario:
| Scenario | Why manual | Example |
| Outcome events | Fire after an async operation succeeds, not on click | track("pipeline_editor.pipeline_actions.save_pipeline_as_completed") |
| Impression events | Fire when something becomes visible (dialog, panel), not from a click | track("pipeline_editor.name_pipeline_dialog_impression") |
| Debounced events | Fire after a delay (e.g. text field editing) | debouncedTrack() using debounce from @/utils/debounce |
| Toggle state | Metadata includes the new value from an onChange callback argument, not known at render time | track("settings.toggle_changed", { new_value: checked }) |
| Non-interactive elements | The click target is not a <button>, <a>, <summary>, or role="button"/role="link" | React Flow's internal Controls callbacks (onZoomIn, etc.) |
import { useAnalytics } from "@/providers/AnalyticsProvider";
const { track } = useAnalytics();
const handleSave = async (name: string) => {
await savePipeline(name);
track("pipeline_editor.pipeline_actions.save_pipeline_as_completed");
};
useEffect(() => {
if (open) {
track("component_editor.save.already_exists_impression");
}
}, [open]);The first is an outcome event — it fires after the save succeeds, not on click. The second is an impression event, firing when the dialog opens.
Event detail shape
The dispatched CustomEvent carries the following detail fields:
| Field | Type | Description |
actionType | string | The action type string (with .click appended for click events) |
metadata | Record<string,unknown> | Optional metadata object |
sessionId | string | Anonymous tab session ID |
route | string | window.location.pathname at time of call |
appVersion | string | undefined | VITE_GIT_COMMIT build variable |
environment | string | undefined | VITE_TANGLE_ENV build variable |
action_type naming convention
Use dot-separated, snake_case segments:
<feature_area>.<entity>[.<sub_entity>].<action_verb>For tracking() data attributes, omit the action verb — the listener appends .click:
<feature_area>.<entity>[.<sub_entity>]For manual track() calls, include the full action verb:
<feature_area>.<entity>[.<sub_entity>].<action_verb>Action verb reference
| Verb | When to use |
click | User explicitly clicked or tapped a button/link (auto-appended by click listener) |
impression | Something became visible for the first time in a session (dialog opened, panel shown) |
completed | An async operation finished successfully |
toggle | User toggled a switch or checkbox |
Rules
- All segments are
snake_case— no camelCase, no hyphens. - The last segment must always be an action verb for manual
track()calls. - For
tracking()helper calls, the last segment is the entity —.clickis appended automatically. - Keep hierarchy shallow — prefer
pipeline.componentoverpipeline.canvas.node.component. - Do not embed counts, IDs, or dynamic values in the
action_typestring. Put them inmetadatainstead.
Examples
# tracking() helper (click listener appends .click)
tracking("header.settings") → header.settings.click
tracking("pipeline_editor.task_node.z_index", { action: "move_forward" })
→ pipeline_editor.task_node.z_index.click
# Manual track() calls
track("pipeline_editor.pipeline_actions.save_pipeline_as_completed") ✓ outcome
track("pipeline_editor.name_pipeline_dialog_impression") ✓ impression
track("settings.toggle_changed", { flag_name: "dashboard" }) ✓ toggle
track("session.tab.start", { flags: { ... } }) ✓ lifecycle
# Bad
track("header.settings_click") ✗ use tracking() helper instead
track("pipeline.run.submit.clicked") ✗ "clicked" is redundantMetadata
Pass an object as the second argument for event-specific properties. Keys should be snake_case. Values must never contain PII (no emails, names, user IDs, or free-form user input).
tracking("pipeline_canvas.tool_bar.auto_layout_select", {
selected_layout: "sugiyama",
page_type: "pipeline_editor",
});
track("settings.secrets.secret_mutated", { action: "created" });Component events
Two namespaces are reserved for the Component Marketplace telemetry baseline:
pipeline_editor.component.*— canvas interactions for a specific component instance (drop, replace, remove, upgrade, duplicate).component_library.*— discovery and library CRUD (search, row click, add, remove, future publish/deprecate).
Standard metadata for component events
Spread componentMetadata(ref, source) from @/utils/componentTracking so every event carries the same identity fields:
import { componentMetadata } from "@/utils/componentTracking";
track("pipeline_editor.component.dropped", {
...componentMetadata(componentRef, "library"),
drop_kind: "new_node",
pipeline_id: pipelineId,
});| Key | Type | Notes |
component_id | string | undefined | Content-addressed digest. Stable across renames. |
component_name | string | undefined | Human-readable name. |
component_source | "user" | "library" | "published" | "url" | "file" | "unknown" | Where the component came from. |
Library mutation events fire from the provider
component_library.added and component_library.removed are emitted from inside ComponentLibraryProvider — callers do not call track() for them. Instead, pass an entryPoint string so the provider can attribute which UI surface triggered the mutation:
await addToComponentLibrary(hydratedComponent, "favorite_button");
await removeFromComponentLibrary(component, "favorite_button");Valid entryPoint values: "favorite_button", "canvas_file_drop", "canvas_file_drop_v2", "import_dialog", "editor_save", "unknown".
Reserved names (do not use yet)
These are documented so future PRs don't collide with the marketplace taxonomy:
component_library.published,component_library.deprecated,component_library.supersededcomponent_library.shared_link.copiedcomponent_collection.created,component_collection.viewed,component_collection.member_addedcomponent_library.search.result.semantic_match
Other versions56 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 56
- TheCraigHewitt/seomachine★ 7.5ksame name
- kostja94/marketing-skills★ 1.1ksame name
- LeoYeAI/openclaw-marketing-skills★ 1ksame name
- mysticaltech/marketingskills★ 352same name
- boraoztunc/skills★ 337same name
- b1rdmania/ghostclaw★ 91same name
- Infinite-Labs-AI/infinite-skills★ 43same name
- robertbstillwell/marketing-skills★ 26same name
- ukunal91app/Marketing-Skills-by-Corey-Haines★ 26same name
- dalroot/agentic-seo★ 23same name
46 more not listed
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.
Used alongsideProjects with it also use operational-enterprise-ai, product-proof-saas and 10 more.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
boraoztuncSkilloperational-enterprise-ai
Create or redesign enterprise AI, automation, security, and operations product pages that explain system boundaries, approvals, auditability, exceptions, and rollback. Use for dark cinematic heroes, hairline grids, metric pauses, expandable solution rows, case-study evidence, security proof, and qualified demo or waitlist handoffs.
Also in 3 projects that use this
boraoztuncSkillproduct-proof-saas
Create or redesign SaaS and AI product landing pages where a real workflow, interface, or deterministic demo is the central proof. Use for pale atmospheric shells, product UI in the hero, prompt-to-output stories, audience tabs, compact feature modules, honest pricing comparisons, and FAQ handoffs.
Also in 3 projects that use this
xfstudioSkillproduct-marketing-context
When the user wants to create or update their product marketing context document. Also use when the user mentions 'product context,' 'marketing context,' 'set up context,' 'positioning,' or wants to avoid repeating foundational information across marketing tasks. Creates `.claude/product-marketing-context.md` that other marketing skills reference.
Also in 20 projects that use this
andremoreira73Skilllinkedin-posts
Draft and refine LinkedIn posts. Use when the user wants to write, improve, or iterate on LinkedIn content.
Also in 3 projects that use this
ai-vitaSkillaso-audit
When the user wants to audit or optimize an App Store or Google Play listing. Also use when the user mentions 'ASO audit,' 'app store optimization,' 'optimize my app listing,' 'improve app visibility,' 'app store ranking,' 'audit my listing,' 'why aren't people downloading my app,' 'improve my app conversion,' 'keyword optimization for app,' or 'compare my app to competitors.' Use when the user shares an App Store or Google Play URL and wants to improve it.
Also in 5 projects that use this
Dragoon0xSkillpricing-page
Present pricing in ways that reduce friction and increase clarity. Handles tier naming, feature comparison, anchor pricing, and plan selection psychology. Use when designing pricing pages, plan selectors, or upgrade prompts.
Also in 3 projects that use this
- +1 more from boraoztunc
In this repository20 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- accessibilitySkill
- address-pr-commentsSkill
- audit-ticketsSkill
- docs-updateSkill
- e2e-testingSkill
- gardeningSkill
- list-skillsSkill
- open-sourceSkill
- project-conventionsSkill
- react-patternsSkill
- reviewSkill
- tangle-domainSkill
- tanstack-querySkill
- tanstack-routerSkill
- typescript-standardsSkill
- ui-primitivesSkill
- validateSkill
- vitest-testingSkill
- playwright-testingSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
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.

