@tour-kit/checklists
v0.11.0
Published
React onboarding checklists with task dependencies, progress tracking & persistence — for getting-started flows and activation.
Maintainers
Readme
@tour-kit/checklists
React onboarding checklists with task dependencies, progress tracking & persistence — for getting-started flows and activation.
Drop-in onboarding checklists, getting-started panels, activation funnels, and setup wizards for React. Tasks support dependencies (one unlocks the next), progress tracking, completion conditions, and headless render-prop variants.
Pro tier — requires a license key. See Licensing.
Alternative to: Appcues checklists, Userpilot checklists, Userflow, Stripe-style activation lists, hand-rolled task panels.
Features
- Task dependencies — auto-locks tasks until prerequisites complete
- Progress tracking —
{ completed, total, percentage }per checklist + aggregate hooks - Completion conditions —
manual,event(listen to custom events), orcustom(predicate) - Conditional visibility —
when()predicate to hide tasks dynamically - Persistence — survives reload via storage adapter
- Headless variants —
ChecklistHeadless,TaskHeadlessfor full UI control - Cycle detection —
hasCircularDependency()catches bad configs - TypeScript-first, supports React 18 & 19
Installation
npm install @tour-kit/checklists @tour-kit/license
# or
pnpm add @tour-kit/checklists @tour-kit/licenseQuick Start
import { LicenseProvider } from '@tour-kit/license'
import {
ChecklistProvider,
Checklist,
ChecklistTask,
ChecklistProgress,
ChecklistPanel,
createChecklist,
createTask,
} from '@tour-kit/checklists'
const onboarding = createChecklist({
id: 'onboarding',
title: 'Get started',
tasks: [
createTask({ id: 'profile', title: 'Complete your profile' }),
createTask({ id: 'invite', title: 'Invite a teammate', dependencies: ['profile'] }),
createTask({ id: 'project', title: 'Create your first project' }),
],
})
function App() {
return (
<LicenseProvider licenseKey={process.env.NEXT_PUBLIC_TOURKIT_LICENSE!}>
<ChecklistProvider checklists={[onboarding]}>
<ChecklistPanel checklistId="onboarding">
<ChecklistProgress />
<Checklist>
<ChecklistTask taskId="profile" />
<ChecklistTask taskId="invite" />
<ChecklistTask taskId="project" />
</Checklist>
</ChecklistPanel>
</ChecklistProvider>
</LicenseProvider>
)
}Mark tasks complete from anywhere:
import { useTask } from '@tour-kit/checklists'
function ProfileForm() {
const { complete, isCompleted } = useTask('profile')
const handleSubmit = async () => {
await saveProfile()
complete()
}
return <form onSubmit={handleSubmit}>{/* ... */}</form>
}i18n & interpolation
All user-facing strings in @tour-kit/checklists accept the {{var | fallback}} interpolation grammar from @tour-kit/core. Wrap your tree in <LocaleProvider> and every task title and description resolves automatically.
import { LocaleProvider } from '@tour-kit/core'
import { ChecklistProvider, ChecklistTask, createTask } from '@tour-kit/checklists'
const greet = createTask({
id: 'greet',
title: { key: 'task.greet' },
description: 'Welcome {{user.name | aboard}}!',
})
<LocaleProvider locale="en" messages={{ 'task.greet': 'Say hi to {{user.name | the team}}' }}>
<ChecklistProvider checklists={[{ id: 'onboarding', tasks: [greet] }]}>
<ChecklistTask taskId="greet" />
</ChecklistProvider>
</LocaleProvider>Full guide: https://usertourkit.com/docs/guides/i18n
Headless variant
import { ChecklistHeadless } from '@tour-kit/checklists'
<ChecklistHeadless checklist={onboarding}>
{({ tasks, progress, completeTask }) => (
<div className="custom-ui">
<progress value={progress.percentage} max={100} />
{tasks.map((task) => (
<button
key={task.id}
disabled={task.locked}
onClick={() => completeTask(task.id)}
>
{task.completed ? '✓' : '○'} {task.title}
</button>
))}
</div>
)}
</ChecklistHeadless>API Reference
Components (styled)
| Export | Purpose |
|---|---|
| ChecklistProvider | Context provider — registers checklists |
| ChecklistPanel | Container with title + collapsible expand |
| Checklist | Task list wrapper |
| ChecklistTask | Single task row |
| ChecklistProgress | Progress bar + percentage |
| ChecklistLauncher | Trigger button to open the panel |
Headless components
import { ChecklistHeadless, TaskHeadless } from '@tour-kit/checklists'Hooks
| Hook | Description |
|---|---|
| useChecklist(id) | Single checklist state + actions |
| useTask(taskId) | Single task state + complete, uncomplete, isCompleted, locked |
| useChecklistPersistence(...) | State recovery via storage adapter |
| useChecklistsProgress() | Aggregate progress across all registered checklists |
| useChecklistContext() | Raw context (advanced) |
Utilities
| Function | Purpose |
|---|---|
| createChecklist(config) | Type-safe checklist factory |
| createTask(config) | Type-safe task factory |
| calculateProgress(state) | { completed, total, percentage } |
| getNextTask(state) | First incomplete and unlocked task |
| getLockedTasks(state) | Tasks blocked by dependencies |
| canCompleteTask(taskId, state) | Are deps satisfied? |
| resolveTaskDependencies(taskId) | Ordered dep list — throws on cycle |
| hasCircularDependency(tasks) | Silent boolean check |
Slot & UI library
import {
Slot, Slottable, UnifiedSlot,
UILibraryProvider, useUILibrary,
} from '@tour-kit/checklists'Types
import type {
ChecklistConfig,
ChecklistState,
ChecklistTaskConfig,
ChecklistTaskState,
ChecklistProgressType, // (component shadows the type name)
ChecklistContextData,
TaskAction,
TaskCompletionCondition, // 'manual' | 'event' | 'custom'
ChecklistProviderConfig,
ChecklistPersistenceConfig,
PersistedChecklistState,
} from '@tour-kit/checklists'Task completion conditions
type TaskCompletionCondition = {
type: 'manual' | 'event' | 'custom'
eventName?: string // for 'event' type
customCheck?: () => boolean // for 'custom' type
}Locked vs invisible tasks
- Locked task — visible, but cannot complete until dependencies finish. Counts toward
totalin progress. - Invisible task —
when()predicate returnedfalse. Excluded fromtotal.
Gotchas
- Circular deps fail differently across APIs.
createChecklist()logs an error;resolveTaskDependencies()throws;hasCircularDependency()is silent. Use the silent check before passing user-built configs. - Render order ≠ dependency order. Tasks render in array order; dependency resolution affects locked state, not display order.
<ChecklistPanel defaultExpanded>writes the expanded state once per mount to avoid render loops with context-scoped callbacks.- Pair with
<LicenseProvider>in production. Dev environments (localhost,127.0.0.1,*.local) bypass the Pro gate.
Related packages
@tour-kit/react— sequential product tours (link tasks to tours)@tour-kit/announcements— modal / toast / banner announcements@tour-kit/adoption— feature adoption tracking@tour-kit/analytics— track checklist completion events@tour-kit/license— required Pro license validation
Documentation
Full documentation: https://usertourkit.com/docs/checklists
License
Pro tier — see LICENSE.md. Requires a Tour Kit Pro license key.
