Fullstory Stable Selectors
π± Platform-Specific Implementation: This document covers core concepts. For implementation details, see:
- Web (JavaScript/TypeScript): SKILL-WEB.md β React, Vue, Angular, Svelte, Next.js, and more
- Mobile: SKILL-MOBILE.md β iOS, Android, Flutter, React Native
Overview
Modern applicationsβboth web and mobileβoften have dynamic, unpredictable element identifiers that change across builds, deployments, or even at runtime. This creates challenges for:
- Fullstory: Reliable search, defined elements, heatmaps
- Automated Testing: Stable E2E test selectors
- Computer User Agents (CUA): AI agents navigating your interface
- Accessibility Tools: Programmatic element identification
The Solution: Add stable, semantic identifiers that describe what the element is, not how it's rendered.
The Universal Problem
Web: Dynamic CSS Classes
<!-- What your code looks like -->
<button className={styles.primaryButton}>Add to Cart</button>
<!-- What renders in the browser -->
<button class="Button_primaryButton__x7Ks2">Add to Cart</button>
β
This hash changes every build!
Mobile: Dynamic View IDs
iOS View Hierarchy:
UIButton (0x7f8b4c0123a0) β Memory address changes every launch
βββ "Add to Cart"
Android View Tree:
Button (id: view-12345) β Auto-generated, unstable
βββ "Add to Cart"
React Native Bridge:
ReactButton (nativeID: rn_7) β Bridge-generated, changes on re-render
Impact Across Platforms
| Tool | Web Problem | Mobile Problem | |------|-------------|----------------| | Fullstory | CSS selectors break | View tree queries unreliable | | E2E Testing | Cypress/Playwright tests brittle | Detox/Espresso tests break | | AI Agents (CUA) | Cannot find elements | Cannot navigate reliably | | Automation | Scripts fail on deploy | Scripts fail on app update |
Why This Matters for AI Agents (CUA)
Computer User AgentsβAI systems that interact with digital interfacesβrely on stable, semantic identifiers to understand and navigate your application.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β HOW CUAs "SEE" YOUR INTERFACE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β β BRITTLE (AI struggles): β
β β
β Web: <button class="sc-3d8f2a btn_primary__xK7n2"> β
β iOS: UIButton at memory 0x7f8b4c0123a0 β
β Android: view-12345 β
β β
β β
SEMANTIC (AI understands): β
β β
β Web: data-component="ProductCard" data-element="purchase-button" β
β iOS: accessibilityIdentifier = "ProductCard.purchase-button" β
β Android: testTag = "ProductCard.purchase-button" β
β β
β The AI can now reliably: β
β β’ Find "the purchase button in ProductCard" β
β β’ Understand the action it will trigger β
β β’ Maintain stable automation across deployments β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Stable selectors provide CUAs with:
- β Consistent element identification across builds
- β Semantic understanding of element purpose
- β Hierarchical context (component β element relationship)
- β Action hints for interaction planning
The Universal Solution
Add stable identifiers that survive build changes and runtime variations:
| Platform | Stable Identifier Mechanism |
|----------|----------------------------|
| Web | data-component, data-element, data-action attributes |
| iOS | accessibilityIdentifier property |
| Android (Kotlin) | contentDescription or resource ID |
| Android (Compose) | testTag modifier, semantics |
| React Native | testID prop |
| Flutter | Key, Semantics widget |
The naming conventions are the same across all platforms β only the implementation mechanism differs.
Core Concepts
The Identifier Taxonomy
Primary Identifiers (Required)
| Concept | Purpose | Naming Convention | Example |
|---------|---------|-------------------|---------|
| Component | Component/screen boundary | PascalCase | ProductCard, CheckoutForm |
| Element | Element role within component | kebab-case | add-to-cart, price-display |
Extended Identifiers (Recommended for CUA/AI)
| Concept | Purpose | When to Use | |---------|---------|-------------| | Action | Describes what happens on interaction | Buttons, links, toggles | | State | Current state of the element | Expandable, toggleable elements | | Variant | Visual or functional variant | A/B tests, feature flags |
Identifier Hierarchy
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SEMANTIC HIERARCHY (applies to all platforms) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Component: "CheckoutForm" β Component boundary β
β β β
β βββ Element: "shipping-section" β Structural element β
β β βββ Element: "address-input" β Interactive element β
β β βββ Element: "city-input" β
β β β
β βββ Element: "payment-section" β
β β βββ Element: "card-input" β
β β Action: "capture-payment" β Action hint for AI β
β β β
β βββ Element: "submit-button" β
β Action: "complete-purchase" β Action hint for AI β
β State: "enabled|disabled|loading" β Current state β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Platform Implementation Quick Reference
| Concept | Web | iOS | Android | React Native | Flutter |
|---------|-----|-----|---------|--------------|---------|
| Component | data-component="X" | accessibilityIdentifier = "X" | testTag("X") | testID="X" | Key("X") |
| Element | data-element="x" | .x suffix | .x suffix | .x suffix | .x suffix |
| Combined | data-component="ProductCard" + data-element="add-to-cart" | "ProductCard.add-to-cart" | "ProductCard.add-to-cart" | "ProductCard.add-to-cart" | Key("ProductCard.add-to-cart") |
Naming Conventions (Universal)
These naming conventions apply to all platforms.
Formal Naming Grammar
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β NAMING GRAMMAR β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Component: [Namespace.]<Domain><Type> β
β β
β Examples: β
β β’ ProductCard (simple) β
β β’ CheckoutPaymentForm (domain + type) β
β β’ Checkout.PaymentForm (namespaced for micro-frontends/modules) β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Element: <subject>-<descriptor>[-<qualifier>] β
β β
β Examples: β
β β’ add-to-cart (action verb) β
β β’ product-image (subject + type) β
β β’ shipping-address-input (subject + descriptor + type) β
β β’ nav-item-products (type + qualifier) β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Action: <verb>[-<object>] β
β β
β Examples: β
β β’ add-item β
β β’ submit-form β
β β’ toggle-menu β
β β’ expand-details β
β β’ navigate-next β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Component Names
Use PascalCase matching your component/class/screen names:
β
GOOD: Matches component names
β’ ProductCard
β’ CheckoutForm
β’ NavigationHeader
β’ UserProfileDropdown
β
GOOD: Namespaced for modules
β’ Checkout.PaymentForm
β’ Catalog.ProductCard
β BAD: Generic names
β’ Container
β’ Wrapper
β’ Component
β’ View
β’ Screen
Element Names
Use kebab-case describing the element's purpose:
β
GOOD: Describes purpose
β’ add-to-cart
β’ email-input
β’ product-image
β’ price-display
β’ main-navigation
β
GOOD: Qualified names for disambiguation
β’ billing-address-line1
β’ shipping-address-line1
β BAD: Describes appearance or position
β’ blue-button
β’ big-button
β’ button-1
β’ first-button
β’ left-sidebar
Action Names
Use verb-first kebab-case describing the outcome:
β
GOOD: Clear action verbs
β’ add-item
β’ submit-order
β’ toggle-filter
β’ navigate-category
β’ expand-details
β BAD: Nouns or unclear
β’ cart
β’ click-handler
β’ button-action
What to Annotate
Always Annotate
- β Buttons and tappable elements β Primary interaction points
- β Form inputs β Text fields, selects, checkboxes, toggles
- β Links and navigation items β Navigation paths
- β Cards and list items β Items in repeating content
- β Modals and dialog triggers β State-changing interactions
- β Tab and accordion controls β Content switchers
Skip Annotation For
- β Pure layout containers β Unless interactive
- β Styling wrappers β Divs/Views for styling only
- β Text-only elements β Unless key analytics content
Best Practices
1. Annotate at Development Time
Add identifiers as you write components, not as an afterthought. This ensures complete coverage.
2. Document Your Conventions
Create a team style guide covering:
- Component naming patterns
- Element naming patterns
- Required annotations
- Platform-specific implementation
3. Combine with Privacy Controls
Stable identifiers and privacy controls work together:
- Annotate sensitive elements for searchability
- Apply privacy masking/exclusion for data protection
- Both serve complementary purposes
4. Use Consistent Depth
Don't over-nest annotations:
β
GOOD: Flat, specific identifiers
ProductCard
βββ add-to-cart
β BAD: Unnecessarily deep
App
βββ MainContent
βββ ProductSection
βββ ProductCard
βββ add-to-cart
5. Use Business Identifiers, Not Positions
For lists and repeating content:
β
GOOD: Business identifier
product-item with productId="SKU-123"
β BAD: Position-based
item-0, item-1, item-2
Integration with Accessibility
Stable selectors complement accessibility attributes:
| Attribute Type | Purpose | Audience | |----------------|---------|----------| | Stable identifiers | Programmatic targeting | Fullstory, Tests, AI Agents | | Accessibility labels | Human-readable description | Screen readers, AI understanding | | Semantic roles | Element type/behavior | Accessibility, AI categorization |
Best Practice: Use BOTH stable identifiers AND accessibility attributes. They serve complementary purposes.
Troubleshooting
Identifiers Not Working
Common issues across all platforms:
- Identifier not set on the element (verify in inspector/debugger)
- Typos in identifier names
- Build tools stripping identifiers in production
- Conditional rendering removing elements
Too Many Search Results
Problem: Searching for "button" returns hundreds of results
Solution: Be more specific with hierarchical identifiers:
- Use
ProductCard.add-to-cartnot justadd-to-cart - Combine component + element for unique targeting
Identifiers Stripped in Production
Check platform-specific build configurations:
- Web: Verify
data-*attributes aren't removed by minifiers - Mobile: Ensure debug-only code isn't stripping identifiers
KEY TAKEAWAYS FOR AGENT
When helping developers implement stable selectors:
Platform Routing
- Detect platform first β Web vs iOS vs Android vs React Native vs Flutter
- Route to implementation file β SKILL-WEB.md or SKILL-MOBILE.md
- Use consistent naming β Same taxonomy applies to all platforms
Core Principles (All Platforms)
- Name by purpose, not appearance: "add-to-cart" not "blue-button"
- Use hierarchical identifiers: Component.element pattern
- Annotate interactive elements: Buttons, inputs, links, cards in lists
- Combine with accessibility: Stable IDs + ARIA/accessibility labels
- Business IDs, not positions:
productId="SKU-123"notitem-0
Questions to Ask Developers
- "What platform(s) are you building for?" (Web, iOS, Android, React Native, Flutter)
- "Are your element identifiers stable across builds?"
- "What elements do you need to reliably find in Fullstory?"
- "Do you have a naming convention for components/screens?"
- "Are you using E2E testing tools?"
- "Is AI/automation tooling on your roadmap?"
Implementation Checklist (All Platforms)
Phase 1: Core Implementation
β‘ Identify interactive elements that need tracking
β‘ Establish naming convention (Component.element pattern)
β‘ Add component identifiers to screens/component roots
β‘ Add element identifiers to buttons, inputs, links, cards
β‘ Use specific, purpose-based names
β‘ Verify identifiers survive production build
β‘ Test in Fullstory search
Phase 2: AI/CUA Readiness
β‘ Add action hints to interactive elements
β‘ Add state indicators for stateful elements
β‘ Ensure accessibility attributes complement stable IDs
β‘ Document naming conventions for team consistency
Phase 3: Enterprise Scale
β‘ Implement type-safe identifier helpers
β‘ Add namespace prefixes for modules/teams
β‘ Add variant tracking for A/B tests
β‘ Configure E2E tools to use stable identifiers
REFERENCE LINKS
Fullstory Documentation
- Element Properties: ../core/fullstory-element-properties/SKILL.md
- Privacy Controls: ../core/fullstory-privacy-controls/SKILL.md
- Test Automation: ./fullstory-test-automation/SKILL.md
Platform-Specific Implementation
- Web Implementation: SKILL-WEB.md
- Mobile Implementation: SKILL-MOBILE.md
Accessibility Standards
- WAI-ARIA Authoring Practices: https://www.w3.org/WAI/ARIA/apg/
- iOS Accessibility: https://developer.apple.com/accessibility/
- Android Accessibility: https://developer.android.com/guide/topics/ui/accessibility
This skill provides the universal foundation for stable selectors across all platforms. See SKILL-WEB.md for web implementation and SKILL-MOBILE.md for mobile implementation.