agentleFS
TangleMLTangleML/tangle-ui/.claude/skills/analytics-tracking/SKILL.md

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
  1. Very specific:

    When a child component renders the interactive element but the parent owns the tracking context, prefer prop drilling or forwarding rest …

    line 40
  2. Distinctive:

    When metadata depends on component state that is known at render time, compute it inline.

    line 78
  3. Very specific:

    Use useAnalytics and call track() directly only when the document-level click listener cannot handle the scenario:

    line 91
  4. Very specific:

    Use dot-separated, snake_case segments:

    line 136
  5. Very specific:

    All segments are snake_case — no camelCase, no hyphens.

    line 165
  6. Very specific:

    Keep hierarchy shallow — prefer pipeline.component over pipeline.canvas.node.component.

    line 168
  7. Very specific:

    Do not embed counts, IDs, or dynamic values in the action_type string.

    line 169

“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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

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:

ScenarioWhy manualExample
Outcome eventsFire after an async operation succeeds, not on clicktrack("pipeline_editor.pipeline_actions.save_pipeline_as_completed")
Impression eventsFire when something becomes visible (dialog, panel), not from a clicktrack("pipeline_editor.name_pipeline_dialog_impression")
Debounced eventsFire after a delay (e.g. text field editing)debouncedTrack() using debounce from @/utils/debounce
Toggle stateMetadata includes the new value from an onChange callback argument, not known at render timetrack("settings.toggle_changed", { new_value: checked })
Non-interactive elementsThe 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:

FieldTypeDescription
actionTypestringThe action type string (with .click appended for click events)
metadataRecord<string,unknown>Optional metadata object
sessionIdstringAnonymous tab session ID
routestringwindow.location.pathname at time of call
appVersionstring | undefinedVITE_GIT_COMMIT build variable
environmentstring | undefinedVITE_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

VerbWhen to use
clickUser explicitly clicked or tapped a button/link (auto-appended by click listener)
impressionSomething became visible for the first time in a session (dialog opened, panel shown)
completedAn async operation finished successfully
toggleUser 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 — .click is appended automatically.
  • Keep hierarchy shallow — prefer pipeline.component over pipeline.canvas.node.component.
  • Do not embed counts, IDs, or dynamic values in the action_type string. Put them in metadata instead.

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 redundant

Metadata

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,
});
KeyTypeNotes
component_idstring | undefinedContent-addressed digest. Stable across renames.
component_namestring | undefinedHuman-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.superseded
  • component_library.shared_link.copied
  • component_collection.created, component_collection.viewed, component_collection.member_added
  • component_library.search.result.semantic_match

Download now (ZIP)

  • Other versions56 projects use a version of this.

    Word for word · 0

    None found.

    Edited · 0

    None found.

    Wrote their own version · 56

    1. TheCraigHewitt/seomachine★ 7.5ksame name
    2. kostja94/marketing-skills★ 1.1ksame name
    3. LeoYeAI/openclaw-marketing-skills★ 1ksame name
    4. mysticaltech/marketingskills★ 352same name
    5. boraoztunc/skills★ 337same name
    6. b1rdmania/ghostclaw★ 91same name
    7. Infinite-Labs-AI/infinite-skills★ 43same name
    8. robertbstillwell/marketing-skills★ 26same name
    9. ukunal91app/Marketing-Skills-by-Corey-Haines★ 26same name
    10. 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.
    • boraoztuncSkill

      operational-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

    • boraoztuncSkill

      product-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

    • xfstudioSkill

      product-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

    • andremoreira73Skill

      linkedin-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-vitaSkill

      aso-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

    • Dragoon0xSkill

      pricing-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.

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.