Feature Adoption Tracking
Track feature usage, measure adoption rates, and nudge users toward underused features with @tour-kit/adoption for React
LLM Context File
Working with an AI assistant? Download the context file for @tour-kit/adoption and paste it into your conversation for accurate code generation.
What is @tour-kit/adoption?
The adoption package helps you understand which features users actually use, identify adoption patterns, and intelligently nudge users toward valuable features they haven't discovered.
Why Track Adoption?
Building features is expensive. Understanding which features drive value and which go unused helps you:
- Prioritize development based on actual usage patterns
- Guide users to features they'll find valuable
- Reduce churn by identifying features users abandon
- Measure product-market fit through adoption metrics
Core Concepts
Feature
A product capability you want to track. Each feature has:
- ID: Unique identifier
- Trigger: How usage is detected (click, event, or callback)
- Criteria: When it's considered "adopted" (default: 3 uses in 30 days)
const feature = {
id: 'dark-mode',
name: 'Dark Mode',
trigger: '#dark-mode-toggle', // CSS selector
adoptionCriteria: { minUses: 3, recencyDays: 30 },
}Adoption Status
Features progress through four states:
- not_started: Never used
- exploring: Used, but below adoption threshold
- adopted: Meets adoption criteria
- churned: Was adopted but hasn't been used recently
Nudge
A contextual prompt shown to encourage feature discovery. The package handles:
- Scheduling: When to show nudges based on cooldowns and session limits
- Prioritization: Which features to nudge first
- Dismissal: Permanent dismissal or temporary snoozing
Installation
pnpm add @tour-kit/adoption @tour-kit/corenpm install @tour-kit/adoption @tour-kit/coreyarn add @tour-kit/adoption @tour-kit/coreQuick Start
import { AdoptionProvider } from '@tour-kit/adoption'
const features = [
{
id: 'dark-mode',
name: 'Dark Mode',
trigger: '#dark-mode-toggle',
category: 'customization',
},
{
id: 'keyboard-shortcuts',
name: 'Keyboard Shortcuts',
trigger: { event: 'shortcuts:opened' },
category: 'productivity',
},
]
export default function RootLayout({ children }) {
return (
<html>
<body>
<AdoptionProvider features={features}>
{children}
</AdoptionProvider>
</body>
</html>
)
}import { IfNotAdopted, NewFeatureBadge } from '@tour-kit/adoption'
export function DarkModeToggle() {
return (
<button id="dark-mode-toggle" className="relative">
Toggle Dark Mode
<IfNotAdopted featureId="dark-mode">
<NewFeatureBadge>New!</NewFeatureBadge>
</IfNotAdopted>
</button>
)
}import { useFeature } from '@tour-kit/adoption'
export function ExportButton() {
const { trackUsage, isAdopted } = useFeature('export')
const handleExport = async () => {
// Your export logic
await exportData()
// Track the usage
trackUsage()
}
return (
<button onClick={handleExport}>
Export Data {!isAdopted && <span className="badge">New</span>}
</button>
)
}Usage Tracking Methods
Automatic Click Tracking
The simplest approach: provide a CSS selector as the trigger.
const feature = {
id: 'search',
name: 'Search',
trigger: '[data-feature="search"]', // Tracks clicks on this element
}Click tracking uses event delegation for performance. Elements can be added/removed dynamically.
Custom Event Tracking
For complex interactions, dispatch custom events:
const feature = {
id: 'export',
name: 'Export',
trigger: { event: 'export:complete' },
}
// In your code
async function handleExport() {
await exportData()
window.dispatchEvent(new CustomEvent('export:complete'))
}Programmatic Tracking
For maximum control, use the useFeature hook:
function AIAssistant() {
const { trackUsage } = useFeature('ai-assist')
const handleGenerate = async () => {
const result = await generateWithAI()
if (result.success) {
trackUsage() // Track only on success
}
}
return <button onClick={handleGenerate}>Generate with AI</button>
}Adoption Criteria
Define what "adopted" means for each feature:
const feature = {
id: 'advanced-search',
name: 'Advanced Search',
trigger: '#advanced-search',
adoptionCriteria: {
minUses: 5, // Must use 5+ times
recencyDays: 30, // Within last 30 days
},
}Custom Adoption Logic
For complex cases, provide a custom function:
const feature = {
id: 'premium-feature',
name: 'Premium Feature',
trigger: '#premium-btn',
adoptionCriteria: {
custom: (usage) => {
// Adopted if used 10+ times OR used in last 7 days
return usage.useCount >= 10 ||
(usage.lastUsed &&
Date.now() - new Date(usage.lastUsed).getTime() < 7 * 24 * 60 * 60 * 1000)
},
},
}Configuration
Full provider configuration:
<AdoptionProvider
features={features}
storage={{
type: 'localStorage',
key: 'my-app-adoption',
}}
nudge={{
enabled: true,
initialDelay: 5000, // Wait 5s before first nudge
cooldown: 86400000, // 24 hours between nudges
maxPerSession: 3, // Max 3 nudges per session
maxFeatures: 1, // Show 1 feature at a time
}}
userId="user-123" // For multi-user tracking
onAdoption={(feature) => {
console.log(`Feature adopted: ${feature.name}`)
}}
onChurn={(feature) => {
console.log(`Feature churned: ${feature.name}`)
}}
/>The userId prop changes the storage key to be user-specific. Omit for single-user applications.