Conditional Components
IfNotAdopted and IfAdopted components: conditionally render content based on whether a user has adopted a specific feature
Overview
IfNotAdopted and IfAdopted are conditional rendering components that show/hide content based on whether a feature has been adopted.
Basic Usage
IfNotAdopted
Show content only if feature is not yet adopted:
import { IfNotAdopted } from '@tour-kit/adoption'
function FeatureToggle() {
return (
<button id="dark-mode-toggle">
Dark Mode
<IfNotAdopted featureId="dark-mode">
<span className="badge">New!</span>
</IfNotAdopted>
</button>
)
}IfAdopted
Show content only if feature is adopted:
import { IfAdopted } from '@tour-kit/adoption'
function FeatureToggle() {
return (
<button id="shortcuts-toggle">
<IfAdopted featureId="shortcuts">
<CheckIcon className="adopted-icon" />
</IfAdopted>
Keyboard Shortcuts
</button>
)
}Props
Both components share the same props:
Prop
Type
Examples
Show/Hide Badges
<div className="feature-list">
<button id="feature-1">
Feature 1
<IfNotAdopted featureId="feature-1">
<span className="badge-new">New</span>
</IfNotAdopted>
</button>
<button id="feature-2">
Feature 2
<IfAdopted featureId="feature-2">
<CheckIcon />
</IfAdopted>
</button>
</div>With Fallback Content
<IfNotAdopted
featureId="export"
fallback={<span className="badge-success">Mastered!</span>}
>
<span className="badge-info">Try this!</span>
</IfNotAdopted>Conditional Help Text
<div className="feature-section">
<h3>Advanced Search</h3>
<IfNotAdopted featureId="advanced-search">
<p className="help-text">
Use filters and operators to find exactly what you need.
Try it now to unlock powerful search capabilities!
</p>
</IfNotAdopted>
<IfAdopted featureId="advanced-search">
<p className="help-text">
You're a search pro! Check out keyboard shortcuts for even faster searches.
</p>
</IfAdopted>
</div>Conditional UI Elements
function Toolbar() {
return (
<div className="toolbar">
<IfNotAdopted featureId="quick-actions">
<button onClick={showTutorial}>
Learn Quick Actions
</button>
</IfNotAdopted>
<IfAdopted featureId="quick-actions">
<QuickActionsPalette />
</IfAdopted>
</div>
)
}Promotional Messaging
<IfNotAdopted featureId="premium-export">
<div className="promo-card">
<h4>Export to PDF</h4>
<p>Save your work as a beautifully formatted PDF</p>
<button>Try Premium Export</button>
</div>
</IfNotAdopted>Progress Indicators
import { useFeature } from '@tour-kit/adoption'
function FeatureCard({ featureId }: { featureId: string }) {
const { feature, usage } = useFeature(featureId)
if (!feature) return null
return (
<div className="card">
<h3>{feature.name}</h3>
<IfNotAdopted featureId={featureId}>
<div className="progress">
<span>
{usage.useCount} / {feature.adoptionCriteria?.minUses || 3} uses
</span>
<div className="progress-bar">
<div
className="progress-fill"
style={{
width: `${(usage.useCount / (feature.adoptionCriteria?.minUses || 3)) * 100}%`,
}}
/>
</div>
</div>
</IfNotAdopted>
<IfAdopted featureId={featureId}>
<div className="success-message">
Feature adopted! Used {usage.useCount} times
</div>
</IfAdopted>
</div>
)
}Contextual Nudges
<div className="feature-container">
<button id="feature-btn">Use Feature</button>
<IfNotAdopted featureId="feature">
<div className="tooltip">
Click here to try this powerful feature!
</div>
</IfNotAdopted>
</div>Multi-Feature Conditions
function Dashboard() {
return (
<div>
{/* Show onboarding if NO features adopted */}
<IfNotAdopted featureId="search">
<IfNotAdopted featureId="export">
<IfNotAdopted featureId="shortcuts">
<OnboardingBanner />
</IfNotAdopted>
</IfNotAdopted>
</IfNotAdopted>
{/* Show congrats if key features adopted */}
<IfAdopted featureId="search">
<IfAdopted featureId="export">
<CongratulationsBanner />
</IfAdopted>
</IfAdopted>
</div>
)
}For complex multi-feature conditions, consider using useAdoptionStats() hook instead.
Animation on State Change
function AnimatedBadge({ featureId }: { featureId: string }) {
return (
<div className="relative">
<IfNotAdopted featureId={featureId}>
<span className="badge animate-pulse">New</span>
</IfNotAdopted>
<IfAdopted featureId={featureId}>
<span className="badge animate-bounce">ā</span>
</IfAdopted>
</div>
)
}Comparison with useFeature
These components are syntactic sugar for the useFeature hook:
// Using IfNotAdopted
<IfNotAdopted featureId="feature">
<Badge>New</Badge>
</IfNotAdopted>
// Equivalent with useFeature
function FeatureBadge() {
const { isAdopted } = useFeature('feature')
if (isAdopted) return null
return <Badge>New</Badge>
}Use conditional components when:
- You only need to show/hide content
- The logic is simple (adopted vs. not adopted)
- You want cleaner JSX
Use useFeature when:
- You need additional data (useCount, status, etc.)
- You need the
trackUsagefunction - You have complex conditional logic
Performance
Both components are lightweight wrappers around useFeature:
// ā Efficient: Only re-renders when adoption status changes
<IfNotAdopted featureId="feature">
<ExpensiveComponent />
</IfNotAdopted>
// ā Also efficient: Multiple instances share the same context
<div>
<IfNotAdopted featureId="feature">...</IfNotAdopted>
<IfAdopted featureId="feature">...</IfAdopted>
</div>TypeScript
Both components are fully typed:
import { IfNotAdopted, IfAdopted } from '@tour-kit/adoption'
<IfNotAdopted featureId="my-feature">
<div>This is typed correctly</div>
</IfNotAdopted>
<IfAdopted
featureId="my-feature"
fallback={<div>Fallback is typed</div>}
>
<div>Children is typed</div>
</IfAdopted>Accessibility
These components render fragments and don't affect accessibility:
// The button remains accessible
<button aria-label="Export data">
Export
<IfNotAdopted featureId="export">
<span aria-label="New feature">New!</span>
</IfNotAdopted>
</button>Ensure content changes are announced to screen readers:
<div aria-live="polite">
<IfNotAdopted featureId="feature">
<p>Try this feature!</p>
</IfNotAdopted>
<IfAdopted featureId="feature">
<p>You've adopted this feature!</p>
</IfAdopted>
</div>Common Patterns
Feature Discovery Flow
function FeatureFlow({ featureId }: { featureId: string }) {
return (
<div>
<IfNotAdopted featureId={featureId}>
<div className="discovery">
<h4>Discover this feature</h4>
<button>Learn More</button>
</div>
</IfNotAdopted>
<IfAdopted featureId={featureId}>
<div className="mastered">
<h4>You've mastered this!</h4>
<button>Share your achievement</button>
</div>
</IfAdopted>
</div>
)
}Gamification
<IfAdopted featureId="power-user-feature">
<div className="achievement">
<TrophyIcon />
<span>Power User Unlocked!</span>
</div>
</IfAdopted>Contextual Help
<IfNotAdopted featureId="feature">
<HelpTooltip>
This feature helps you do X. Click to try it!
</HelpTooltip>
</IfNotAdopted>Best Practices
- Use semantic HTML inside conditional components:
// Good
<IfNotAdopted featureId="feature">
<span className="badge">New</span>
</IfNotAdopted>
// Bad (creates unnecessary wrapper)
<IfNotAdopted featureId="feature">
<div>
<div>
<span>New</span>
</div>
</div>
</IfNotAdopted>- Provide fallback for important UI:
<IfNotAdopted featureId="feature" fallback={<Placeholder />}>
<FeatureContent />
</IfNotAdopted>- Avoid deep nesting:
// Good: Use useAdoptionStats for complex conditions
const { byStatus } = useAdoptionStats()
if (byStatus.adopted.length === 0) {
return <OnboardingBanner />
}
// Bad: Too deeply nested
<IfNotAdopted featureId="a">
<IfNotAdopted featureId="b">
<IfNotAdopted featureId="c">
<OnboardingBanner />
</IfNotAdopted>
</IfNotAdopted>
</IfNotAdopted>Next Steps
- useFeature Hook - More adoption state control
- NewFeatureBadge - Pre-built badge component
- FeatureButton - Button with tracking