AdoptionProvider
AdoptionProvider: configure feature definitions, usage thresholds, nudge rules, and storage for the adoption tracking system
Overview
AdoptionProvider is the root context provider that manages feature tracking, usage persistence, and nudge scheduling. Wrap your application with this provider to enable adoption tracking.
Basic Usage
import { AdoptionProvider } from '@tour-kit/adoption'
const features = [
{
id: 'dark-mode',
name: 'Dark Mode',
trigger: '#dark-mode-toggle',
},
{
id: 'export',
name: 'Export Data',
trigger: { event: 'export:complete' },
},
]
export default function RootLayout({ children }) {
return (
<AdoptionProvider features={features}>
{children}
</AdoptionProvider>
)
}Props
Prop
Type
Feature Configuration
Each feature in the features array defines what to track and when it's considered adopted.
Basic Feature
const feature = {
id: 'search',
name: 'Search',
trigger: '[data-feature="search"]',
}Complete Feature Definition
const feature = {
id: 'advanced-search',
name: 'Advanced Search',
trigger: '#advanced-search-btn',
adoptionCriteria: {
minUses: 5,
recencyDays: 30,
},
category: 'productivity',
description: 'Find exactly what you need with filters and operators',
priority: 10,
premium: true,
resources: {
tourId: 'advanced-search-tour',
hintIds: ['search-filter-hint', 'search-operator-hint'],
},
}Prop
Type
Trigger Types
CSS Selector (Click Tracking)
Track clicks on elements matching the selector:
const feature = {
id: 'sidebar',
name: 'Sidebar',
trigger: '[data-sidebar-toggle]',
}The provider automatically sets up event delegation to track clicks. Elements can be added/removed dynamically.
Use data attributes for reliable tracking: data-feature="name" is better than classes that might change.
Custom Event
Track custom events dispatched in your code:
const feature = {
id: 'pdf-export',
name: 'PDF Export',
trigger: { event: 'pdf:exported' },
}
// In your component
async function handleExport() {
await generatePDF()
window.dispatchEvent(new CustomEvent('pdf:exported'))
}Callback (Polling)
For complex conditions, provide a callback that's polled periodically:
const feature = {
id: 'full-screen',
name: 'Full Screen Mode',
trigger: {
callback: () => document.fullscreenElement !== null,
},
}Callbacks are polled every 1 second. Keep them lightweight to avoid performance issues.
Storage Configuration
LocalStorage (Default)
<AdoptionProvider
features={features}
storage={{
type: 'localStorage',
key: 'my-app-adoption', // Default: 'tour-kit-adoption'
}}
/>Memory Storage (Testing)
For tests or temporary sessions:
<AdoptionProvider
features={features}
storage={{
type: 'memory',
}}
/>Custom Storage Adapter
Integrate with your own storage:
const customStorage = {
getItem: async (key: string) => {
return await db.settings.get(key)
},
setItem: async (key: string, value: string) => {
await db.settings.set(key, value)
},
removeItem: async (key: string) => {
await db.settings.delete(key)
},
}
<AdoptionProvider
features={features}
storage={{
type: 'custom',
adapter: customStorage,
}}
/>Custom adapters can be async. The provider handles loading states automatically.
Nudge Configuration
Control when and how nudges are shown:
<AdoptionProvider
features={features}
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 nudge at a time
}}
/>Prop
Type
Disabling Nudges
To track adoption without showing nudges:
<AdoptionProvider
features={features}
nudge={{ enabled: false }}
/>Event Callbacks
Track adoption milestones in your analytics:
<AdoptionProvider
features={features}
onAdoption={(feature) => {
analytics.track('Feature Adopted', {
featureId: feature.id,
featureName: feature.name,
category: feature.category,
})
}}
onChurn={(feature) => {
analytics.track('Feature Churned', {
featureId: feature.id,
featureName: feature.name,
})
}}
onNudge={(feature, action) => {
analytics.track('Nudge Interaction', {
featureId: feature.id,
action, // 'shown', 'clicked', or 'dismissed'
})
}}
/>For automatic analytics integration, see the Analytics Guide.
Multi-User Tracking
Use the userId prop to track adoption per user:
function App() {
const { userId } = useAuth()
return (
<AdoptionProvider
features={features}
userId={userId} // Storage key becomes: tour-kit-adoption-user-123
>
{children}
</AdoptionProvider>
)
}This is essential for:
- Multi-user applications
- Different adoption states per user
- User-specific nudge history
Changing userId clears the current adoption state and loads the new user's data. Use stable IDs.
TypeScript
The provider is fully typed:
import type { Feature, AdoptionProviderProps } from '@tour-kit/adoption'
const features: Feature[] = [
{
id: 'feature-1',
name: 'Feature 1',
trigger: '#btn',
},
]
const config: AdoptionProviderProps = {
features,
storage: { type: 'localStorage' },
nudge: { enabled: true },
onAdoption: (feature) => {
// feature is typed as Feature
},
}Accessibility
The provider:
- Does not render any UI itself (fully headless)
- Tracks usage without interfering with user interactions
- Supports keyboard navigation when using trigger selectors
- Respects
prefers-reduced-motionfor nudge animations
Best Practices
- Define features at the module level to prevent re-initialization:
// Good: Stable reference
const features = [{ id: 'search', name: 'Search', trigger: '#search' }]
function App() {
return <AdoptionProvider features={features}>...</AdoptionProvider>
}
// Bad: New array every render
function App() {
return (
<AdoptionProvider features={[{ id: 'search', name: 'Search', trigger: '#search' }]}>
...
</AdoptionProvider>
)
}- Use specific selectors to avoid false positives:
// Good
trigger: '[data-feature="export-pdf"]'
// Bad (too generic, might match other buttons)
trigger: 'button'- Set appropriate cooldowns to avoid annoying users:
nudge: {
cooldown: 86400000, // 24 hours minimum
maxPerSession: 2, // Don't overwhelm
}- Test adoption criteria with real usage patterns:
// Too aggressive: Users will hit this accidentally
adoptionCriteria: { minUses: 1 }
// Better: Indicates deliberate usage
adoptionCriteria: { minUses: 3, recencyDays: 30 }Next Steps
- useFeature Hook - Track individual features
- useNudge Hook - Manage nudge state
- Analytics Integration - Automatic analytics tracking