Streaks
Track daily activity with configurable streak mechanics
Leaderboards
Rank users by any metric with time-based competitions
Achievements
Unlock badges and milestones based on user actions
Code First
Define config in code, auto-synced to platform
Quick Start
Define Configuration
Create your engagement config in engagement.config.ts:
Wrap Your App
Add <EngagementProvider> to enable hooks and automatic sync.
Record Activity
Use hooks like useStreak() and useLeaderboard() to track user progress.
Configuration
Define your streaks, leaderboards, and achievements in a single config file:
import { defineEngagement, streak, leaderboard, achievement } from '@sylphx/sdk/engagement'
export const engagement = defineEngagement({
// Streak configurations
streaks: {
daily_login: streak({
name: 'Daily Login',
description: 'Log in every day',
resetAfterDays: 1, // Reset after 1 day of inactivity
gracePeriodHours: 4, // 4-hour grace period
milestones: [7, 30, 100, 365],
}),
workout_streak: streak({
name: 'Workout Streak',
description: 'Complete a workout',
resetAfterDays: 1,
gracePeriodHours: 0,
milestones: [7, 14, 30],
}),
},
// Leaderboard configurations
leaderboards: {
weekly_points: leaderboard({
name: 'Weekly Points',
description: 'Top point earners this week',
period: 'weekly',
sortOrder: 'desc',
maxEntries: 100,
}),
all_time_score: leaderboard({
name: 'All-Time Score',
period: 'alltime',
sortOrder: 'desc',
}),
},
// Achievement configurations
achievements: {
first_steps: achievement({
name: 'First Steps',
description: 'Complete your first task',
icon: 'footprints',
tier: 'bronze',
points: 10,
}),
power_user: achievement({
name: 'Power User',
description: 'Use the app for 30 days',
icon: 'zap',
tier: 'gold',
points: 100,
secret: false,
}),
},
})Code First Sync
Provider Setup
Wrap your app with the EngagementProvider to enable engagement features:
import { SylphxProvider } from '@sylphx/sdk/react'
import { engagement } from './engagement.config'
export default function RootLayout({ children }) {
return (
<SylphxProvider
appId={process.env.NEXT_PUBLIC_SYLPHX_APP_ID}
engagement={engagement} // Pass engagement config
>
{children}
</SylphxProvider>
)
}Streaks
Track daily user activity with configurable streak mechanics:
'use client'
import { useStreak } from '@sylphx/sdk/react'
export function StreakCard() {
const { streak, recordActivity, isLoading } = useStreak('daily_login')
return (
<div className="p-6 border rounded-xl">
<div className="flex items-center gap-3">
<span className="text-3xl">🔥</span>
<div>
<div className="text-2xl font-bold">{streak?.current ?? 0} days</div>
<div className="text-muted-foreground">Current streak</div>
</div>
</div>
<div className="mt-4 text-sm text-muted-foreground">
Best: {streak?.best ?? 0} days
</div>
<button
onClick={() => recordActivity()}
disabled={isLoading || streak?.recordedToday}
className="mt-4 w-full btn btn-primary"
>
{streak?.recordedToday ? 'Logged Today ✓' : 'Record Activity'}
</button>
</div>
)
}import { platform } from '@/lib/platform'
// Record streak activity
const result = await platform.engagement.recordStreak({
userId: user.id,
streakId: 'daily_login',
})
// result: { current: 5, best: 12, recordedToday: true, milestone: null }
// Get user's streak status
const streak = await platform.engagement.getStreak(user.id, 'daily_login')
// Check if milestone reached
if (result.milestone) {
// User hit 7, 30, 100, or 365 day milestone!
await sendNotification(user.id, `🎉 ${result.milestone} day streak!`)
}| Property | Type | Description |
|---|---|---|
resetAfterDays | number | Days of inactivity before streak resets (default: 1) |
gracePeriodHours | number | Extra hours before reset (for timezone flexibility) |
milestones | number[] | Day counts that trigger milestone events |
Leaderboards
Create competitive rankings with automatic period resets:
'use client'
import { useLeaderboard } from '@sylphx/sdk/react'
export function WeeklyLeaderboard() {
const { entries, userRank, submitScore, isLoading } = useLeaderboard('weekly_points')
return (
<div className="p-6 border rounded-xl">
<h3 className="text-lg font-bold mb-4">🏆 Weekly Leaderboard</h3>
{/* Your rank */}
{userRank && (
<div className="mb-4 p-3 bg-primary/10 rounded-lg">
Your rank: #{userRank.rank} ({userRank.score} pts)
</div>
)}
{/* Top players */}
<div className="space-y-2">
{entries?.slice(0, 10).map((entry, i) => (
<div key={entry.userId} className="flex items-center gap-3 p-2">
<span className="w-6 text-center font-bold">
{i === 0 ? '🥇' : i === 1 ? '🥈' : i === 2 ? '🥉' : `#${i + 1}`}
</span>
<span className="flex-1">{entry.displayName}</span>
<span className="font-mono">{entry.score}</span>
</div>
))}
</div>
</div>
)
}import { platform } from '@/lib/platform'
// Submit a score
await platform.engagement.submitScore({
userId: user.id,
leaderboardId: 'weekly_points',
score: 150,
metadata: { source: 'completed_task' },
})
// Get leaderboard with pagination
const leaderboard = await platform.engagement.getLeaderboard('weekly_points', {
limit: 100,
offset: 0,
})
// Get user's rank
const userRank = await platform.engagement.getUserRank(user.id, 'weekly_points')
// { rank: 5, score: 450, percentile: 95 }| Property | Type | Description |
|---|---|---|
period | 'daily' | 'weekly' | 'monthly' | 'alltime' | How often the leaderboard resets |
sortOrder | 'asc' | 'desc' | Sort direction (desc = highest first) |
maxEntries | number | Maximum entries to track (default: 1000) |
Achievements
Award badges and milestones based on user actions:
'use client'
import { useAchievements } from '@sylphx/sdk/react'
export function AchievementGrid() {
const { achievements, unlockedIds, totalPoints } = useAchievements()
return (
<div>
<div className="mb-4 text-lg font-bold">
🏅 {totalPoints} points earned
</div>
<div className="grid grid-cols-3 gap-4">
{achievements?.map((achievement) => {
const isUnlocked = unlockedIds?.includes(achievement.id)
return (
<div
key={achievement.id}
className={`p-4 border rounded-xl text-center ${
isUnlocked ? 'bg-primary/10 border-primary' : 'opacity-50'
}`}
>
<div className="text-3xl mb-2">
{isUnlocked ? achievement.icon : '🔒'}
</div>
<div className="font-medium">
{achievement.secret && !isUnlocked ? '???' : achievement.name}
</div>
<div className="text-xs text-muted-foreground">
{achievement.points} pts
</div>
</div>
)
})}
</div>
</div>
)
}import { platform } from '@/lib/platform'
// Unlock an achievement
const result = await platform.engagement.unlockAchievement({
userId: user.id,
achievementId: 'first_steps',
})
if (result.unlocked) {
// Achievement was newly unlocked
// result.achievement contains full achievement data
await sendNotification(user.id, `🏆 Achievement unlocked: ${result.achievement.name}`)
}
// Get all user achievements
const userAchievements = await platform.engagement.getUserAchievements(user.id)
// { unlocked: ['first_steps', 'power_user'], totalPoints: 110 }
// Check if user has achievement
const hasAchievement = await platform.engagement.hasAchievement(user.id, 'power_user')| Property | Type | Description |
|---|---|---|
tier | 'bronze' | 'silver' | 'gold' | 'platinum' | Achievement rarity/difficulty tier |
points | number | Points awarded when unlocked |
secret | boolean | Hide details until unlocked (default: false) |
icon | string | Icon identifier for display |
Event Integration
Automatically trigger engagement updates from analytics events:
// In your engagement config, link to analytics events
export const engagement = defineEngagement({
streaks: {
daily_activity: streak({
name: 'Daily Activity',
// Auto-record when any of these events fire
triggers: ['page_view', 'task_complete', 'workout_done'],
}),
},
achievements: {
first_purchase: achievement({
name: 'First Purchase',
// Auto-unlock when this event fires
trigger: 'purchase_complete',
}),
share_master: achievement({
name: 'Share Master',
// Unlock after 10 share events
trigger: { event: 'content_shared', count: 10 },
}),
},
})Combine with Analytics
analytics.track() can automatically trigger streak recordings and achievement unlocks.Progress Tracking
Track user progress toward goals with visual progress bars:
'use client'
import { useProgress } from '@sylphx/sdk/react'
export function ProgressCard() {
const { progress, increment, isLoading } = useProgress('onboarding')
return (
<div className="p-6 border rounded-xl">
<h3 className="font-bold mb-2">Onboarding Progress</h3>
<div className="h-3 bg-muted rounded-full overflow-hidden">
<div
className="h-full bg-primary transition-all"
style={{ width: `${progress?.percentage ?? 0}%` }}
/>
</div>
<div className="mt-2 text-sm text-muted-foreground">
{progress?.current ?? 0} / {progress?.target ?? 0} steps complete
</div>
</div>
)
}Privacy Controls
Control data visibility and opt-out options:
// User privacy preferences
await platform.engagement.setPrivacy(userId, {
showOnLeaderboards: false, // Hide from public leaderboards
shareAchievements: false, // Don't share achievement unlocks
allowDataExport: true, // Allow data export requests
})
// Anonymize leaderboard display
const leaderboard = await platform.engagement.getLeaderboard('weekly', {
anonymize: true, // Shows "Player #123" instead of real names
})
// GDPR: Export all engagement data
const data = await platform.engagement.exportUserData(userId)
// GDPR: Delete all engagement data
await platform.engagement.deleteUserData(userId)Deep Dive Guides
Learn more about each engagement feature with detailed guides and advanced patterns:
Streaks Guide
Grace periods, recovery mechanics, milestones, and automatic triggers
Leaderboards Guide
Time periods, aggregation, rewards, and privacy controls
Achievements Guide
Tiers, progress tracking, secret achievements, and auto-unlock triggers
React Components
Pre-built UI components for streaks, leaderboards, and achievements