---
name: pendo-analytics-tagging
description: >
  Add stable pendo-analytics attributes to React components so Pendo can reliably track user
  interactions and power in-app guides. Use this skill ANY TIME an engineer wants to instrument
  their React app for Pendo, improve tagging stability, audit missing Pendo attributes, or set up
  a pendo-analytics naming convention. Trigger on phrases like "add pendo attributes", "instrument
  my app for pendo", "pendo tagging", "add pendo-analytics", "pendo analytics setup", "my pendo
  tags keep breaking", "set up pendo tracking", "pendo feature tagging", or any request to make
  a React codebase more reliably trackable by Pendo. Also trigger when an engineer is setting up
  Pendo for the first time, or a PM asks the team to add Pendo attributes before a tagging session.
---

# Pendo Analytics Tagging

Help engineers add `pendo-analytics` attributes to their React codebase so Pendo can reliably
identify UI elements — powering analytics, in-app guides, session replay, feedback, surveys,
and everything else the product team uses Pendo for.

---

## Why This Matters: A 60-Second Brief for Engineers

**What is Pendo?**
Pendo is a product experience platform that sits on top of your web or mobile app. It reads the
DOM to understand what users are doing, and that single layer of instrumentation powers
everything the product team uses it for:

- **Understand the product experience** — analytics, session replay, and surveys show what
  users do, where they struggle, and how they feel about it
- **Communicate at scale** — in-app guides, tooltips, and cross-channel journeys reach users
  at the right moment without a code deploy
- **Listen to customers** — feedback from every channel (in-app, calls, support tickets) is
  aggregated, clustered by AI, and connected to the roadmap

All of these depend on the same thing: Pendo's ability to **reliably identify the UI elements
in your app**. When that identification breaks, the product team loses visibility, communication,
and customer signal across the board.

**The tagging problem**
Pendo identifies UI elements using CSS selectors — by default it looks at `class` names, element
`id`s, and DOM structure. This works fine until your codebase changes: a CSS refactor, a
component rename, a framework upgrade, or Tailwind's JIT compiler all silently break Pendo's
element matching. Analytics flatline, guides stop appearing, session replays lose context,
and surveys miss their target — all with no error in the console. Your product team often
doesn't notice until days of data are already missing.

**The fix: stable custom attributes**
The solution is to add a dedicated `pendo-analytics` attribute to every clickable element you
want Pendo to track. This attribute:
- Is invisible to users and has no effect on styling or behaviour
- Survives CSS refactors, component renames, and class changes
- Gives Pendo an unambiguous, human-readable anchor for every capability that needs to
  identify a UI element
- Enables Pendo's **Automatic Feature Tagging** — new values are picked up automatically
  without anyone having to manually tag them

Think of it as the same reason QA engineers add `data-testid` — except instead of your test
suite reading it, the entire Pendo platform reads it. And if you already have `data-testid`
in your codebase, great — Pendo can use those too. But `pendo-analytics` gives you a
dedicated, decoupled attribute that won't break if QA changes their test IDs.

---

## Naming Convention: `pendo-analytics`

Every interactive element that should be tracked gets a single `pendo-analytics` attribute.

### Format

Describe what the element is or does, in lowercase kebab-case. Keep it short, human-readable,
and unique within your app.

```
collapse-nav-item
upgrade-plan
export-csv
billing-settings
create-new-report
```

### Rules

- **Lowercase kebab-case only** — no camelCase, no underscores, no spaces
- **Describe the purpose** — someone reading the value should immediately know what the element does (`collapse-nav-item` not `btn-1`)
- **Don't include the element type** — the HTML tag already tells you it's a button or link. `upgrade-plan` not `upgrade-plan-button`
- **Stable** — don't include dynamic values like user IDs, timestamps, or counts
- **Unique within your app** — two different features should never share the same `pendo-analytics`
- **Only on clickable elements** — buttons, links, and clickable containers, not form inputs or structural wrappers

### Examples

```jsx
// ✅ Good
<button pendo-analytics="collapse-nav-item">Collapse</button>
<button pendo-analytics="upgrade-plan">Upgrade Plan</button>
<button pendo-analytics="export-csv">Export CSV</button>
<a pendo-analytics="billing-settings">Billing</a>
<button pendo-analytics="create-new-report">New Report</button>
<button pendo-analytics="dismiss-onboarding-banner">Dismiss</button>

// ❌ Bad — dynamic, unstable, or non-descriptive
<button pendo-analytics={`button-${userId}`}>...</button>   // dynamic
<button pendo-analytics="btn-1">...</button>                // meaningless
<button pendo-analytics="submitButton">...</button>         // camelCase
<div pendo-analytics="card-wrapper">...</div>               // not interactive
<input pendo-analytics="search-global" />                   // form element, not a click target
```

---

## What Elements to Tag

Tag elements a user **clicks on**. In order of priority:

1. **Primary CTAs** — the main action on any given page or modal
2. **Navigation items** — top nav, sidebar links, tabs
3. **Secondary actions** — cancel, back, skip, dismiss
4. **Feature entry points** — anything that opens a flow, modal, or feature
5. **Repeated interactive items** — list rows, table actions, card buttons (use a stable pattern, see below)

**Don't tag** form inputs, selects, textareas, or other non-click elements — `pendo-analytics`
is for tracking clickable interactions, not form field usage.

### Handling Repeated Elements

For elements that repeat (e.g. rows in a table), use a consistent pattern that avoids dynamic IDs:

```jsx
// ✅ Tag the action, not the row — keeps it stable even as data changes
{integrations.map((integration) => (
  <tr key={integration.id}>
    <td>{integration.name}</td>
    <td>
      <button pendo-analytics="connect-integration">
        Connect
      </button>
    </td>
  </tr>
))}
```

If individual rows *must* be distinguishable, use a slug-based (not ID-based) suffix:

```jsx
// ✅ Slug from a stable name field, not a database ID
<button pendo-analytics={`connect-${integration.slug}`}>
  Connect
</button>
```

---

## Step-by-Step: Adding Attributes to a Codebase

### Step 1 — Define your ID schema

Before touching any code, agree on the area names used in your product. Paste or describe the
main sections of your app and I'll draft a schema. For example:

```
Areas: nav, dashboard, reports, settings, billing, onboarding, modals
```

Record this in `docs/pendo-id-schema.md` (I'll create this for you).

### Step 2 — Audit the codebase

I'll scan your React components for clickable elements that are missing `pendo-analytics`.
The elements I'll look for:

```
<button>
<a href=...>
Custom components that render clickable elements (e.g. <Button>, <Link>, <IconButton>)
```

To run the audit, tell me:
- The path to your components directory (e.g. `src/components`, `app/`, `frontend/src/`)
- Any custom component names that wrap interactive elements (e.g. your design system's `<Button>`)

### Step 3 — Review and confirm the ID list

I'll produce a list of proposed `pendo-analytics` values for each element I find, grouped by
area/page. Review and adjust names before I write any code. You stay in control of the schema.

### Step 4 — Apply the attributes

Once confirmed, I'll apply the attributes across the codebase — modifying JSX directly.
I'll show diffs before writing to any file.

### Step 5 — Register attributes in Pendo

After deploying, a Pendo subscription admin needs to register the attribute in Pendo settings:

```
Settings → Subscription settings → Applications → [Your App]
→ Tagging and Guide Settings → Collect custom HTML attributes
→ Add: pendo-analytics
```

Once registered, Pendo collects the attribute from raw events within 15–30 minutes.
From that point, your Pendo product team can use it for feature tagging and guide targeting —
and can enable **Automatic Feature Tagging** so new `pendo-analytics` values you ship are
picked up automatically.

---

## Pendo ID Schema Document

When starting a new project, I'll create `docs/pendo-id-schema.md` in the repo:

```markdown
# Pendo Analytics Attribute Registry

Custom HTML attribute: `pendo-analytics`
Convention: lowercase kebab-case describing what the element does

## Registered Values

| pendo-analytics | Element | Location |
|---|---|---|
| collapse-nav-item | <button> | SideNav.tsx |
| upgrade-plan | <button> | TopNav.tsx |
| export-csv | <button> | ReportsPage.tsx |
| ... | ... | ... |
```

This becomes the source of truth shared between engineering and the product/analytics team.

---

## Common Questions

**"Will this affect performance or bundle size?"**
No. HTML data attributes are zero-cost — the browser ignores unknown attributes, and they add
negligible bytes to the DOM.

**"Should we strip them in production?"**
No — Pendo needs to read them in production. Unlike `data-testid`, these attributes are
*intentionally* present in production builds.

**"What if our component library already uses data-testid?"**
You can use both. `pendo-analytics` and `data-testid` serve different audiences (Pendo vs. your
test suite) and can coexist on the same element:
```jsx
<button data-testid="save-btn" pendo-analytics="save-changes">Save</button>
```

**"Do we need to update Pendo every time we add a new attribute?"**
Only once — register `pendo-analytics` (or `data-pendo-*`) as a wildcard in Pendo settings.
After that, every new `pendo-analytics` value you ship is automatically collected.

**"Our component library wraps native elements — do we tag the wrapper or the native element?"**
Tag the outermost component that engineers interact with in JSX. If your `<Button>` component
passes through unknown props (spread `{...props}`), the attribute will reach the native element
automatically. If it doesn't, add explicit prop forwarding:
```jsx
// In your Button component
const Button = ({ children, 'pendo-analytics': pendoId, ...props }) => (
  <button pendo-analytics={pendoId} {...props}>{children}</button>
);
```

---

## References

- `references/tagging-patterns.md` — Framework-specific patterns and edge cases
- Pendo Help: Custom HTML attributes — https://support.pendo.io/hc/en-us/articles/21346265192603
- Pendo Help: Automatic Feature Tagging — https://support.pendo.io/hc/en-us/articles/20116813908763
