React
Examples & Recipes
This page provides practical examples and recipes for using OnboardJS in your React applications. See how to implement common onboarding patterns, customize step rendering, persist progress, and more.
1. Basic Linear Onboarding Flow
tsx
70 lines
import { OnboardingProvider, useOnboarding } from '@onboardjs/react'
const steps = [
{
id: 'welcome',
type: 'INFORMATION',
payload: { title: 'Welcome!' },
nextStep: 'profile',
},
{
id: 'profile',
type: 'SINGLE_CHOICE',
payload: {
question: 'Your role?',
options: [{ id: 'dev', label: 'Developer', value: 'dev' }],
},
nextStep: 'done',
},
{ id: 'done', type: 'INFORMATION', payload: { title: 'All set!' } },
]
const componentRegistry = {
INFORMATION: ({ payload }) => <div>{payload.title}</div>,
SINGLE_CHOICE: ({ payload, next }) => (
<div>
<h2>{payload.question}</h2>
{payload.options.map((option) => (
<button key={option.id} onClick={() => next({ answer: option.value })}>
{option.label}
</button>
))}
</div>
),
}
function OnboardingUI() {
const { currentStep, state, next, previous } = useOnboarding()
if (state.isCompleted) return <div>Onboarding complete!</div>
const Component =
componentRegistry[currentStep.payload.componentKey ?? currentStep.type] ||
(() => <div>Unknown step type</div>)
return (
<div>
<Component
payload={currentStep.payload}
coreContext={state.context}
onDataChange={() => {}}
/>
<div>
<button onClick={previous} disabled={!state.canGoPrevious}>
Back
</button>
<button onClick={next} disabled={!state.canGoNext}>
Next
</button>
</div>
</div>
)
}
export default function App() {
return (
<OnboardingProvider steps={steps}>
<OnboardingUI />
</OnboardingProvider>
)
}
2. Branching Flow Based on User Input
tsx
35 lines
const steps = [
{
id: 'start',
type: 'INFORMATION',
payload: { title: 'Start' },
nextStep: 'choose',
},
{
id: 'choose',
type: 'SINGLE_CHOICE',
payload: {
question: 'Are you a developer?',
dataKey: 'isDeveloper',
options: [
{ id: 'yes', label: 'Yes', value: true },
{ id: 'no', label: 'No', value: false },
],
},
nextStep: (context) =>
context.answers?.isDeveloper ? 'dev-setup' : 'user-setup',
},
{
id: 'dev-setup',
type: 'INFORMATION',
payload: { title: 'Dev Setup' },
nextStep: 'done',
},
{
id: 'user-setup',
type: 'INFORMATION',
payload: { title: 'User Setup' },
nextStep: 'done',
},
{ id: 'done', type: 'INFORMATION', payload: { title: 'Finished!' } },
]
3. Custom Step Component
tsx
26 lines
function CustomSurveyStep({ step, payload, next, updateContext }) {
const [answer, setAnswer] = React.useState('')
const handleSubmit = async () => {
await updateContext({ survey: answer })
await next()
}
return (
<div>
<h2>{payload.title}</h2>
<input value={answer} onChange={(e) => setAnswer(e.target.value)} />
<button onClick={handleSubmit}>Continue</button>
</div>
)
}
const componentRegistry = {
CUSTOM_SURVEY: CustomSurveyStep,
// ...other mappings
}
<OnboardingProvider steps={steps}>
{/** Render the onboarding UI with custom components like above */}
<OnboardingUI />
</OnboardingProvider>
4. Persisting Progress with localStorage
tsx
9 lines
<OnboardingProvider
steps={steps}
localStoragePersistence={{
key: 'onboardjs:my-onboarding',
ttl: 1000 * 60 * 60 * 24, // 1 day
}}
>
<OnboardingUI />
</OnboardingProvider>
5. Integrating with Supabase or Neon
tsx
19 lines
<OnboardingProvider
steps={steps}
customOnDataLoad={async () => {
const { data } = await supabase
.from('onboarding')
.select('context')
.eq('user_id', user.id)
.single()
return data?.context || undefined
}}
customOnDataPersist={async (context) => {
await supabase.from('onboarding').upsert({ user_id: user.id, context })
}}
customOnClearPersistedData={async () => {
await supabase.from('onboarding').delete().eq('user_id', user.id)
}}
>
<OnboardingUI />
</OnboardingProvider>
6. Handling Flow Completion
tsx
13 lines
<OnboardingProvider
steps={steps}
onFlowComplete={(context) => {
// Send analytics, redirect, or show a custom message
console.log('Onboarding finished!', context)
toast('Onboarding Complete!', {
description: `Welcome, ${context.flowData?.userName || 'friend'}! You're all set.`,
duration: 3000,
})
}}
>
<OnboardingUI />
</OnboardingProvider>
7. Using Checklist Steps
tsx
16 lines
const steps = [
{
id: 'checklist',
type: 'CHECKLIST',
payload: {
dataKey: 'setupTasks',
items: [
{ id: 'profile', label: 'Complete your profile', isMandatory: true },
{ id: 'invite', label: 'Invite a teammate', isMandatory: false },
],
minItemsToComplete: 1,
},
nextStep: 'done',
},
{ id: 'done', type: 'INFORMATION', payload: { title: 'All done!' } },
]
8. Customizing Navigation Buttons
tsx
15 lines
function CustomNav() {
const { state, next, previous, skip } = useOnboarding()
return (
<div>
<button onClick={previous} disabled={!state.canGoPrevious}>
Back
</button>
{state.isSkippable && <button onClick={skip}>Skip</button>}
<button onClick={next} disabled={!state.canGoNext}>
Next
</button>
</div>
)
}
9. Accessing and Updating Context
tsx
12 lines
function ShowUserName() {
const { state, updateContext } = useOnboarding()
return (
<div>
<p>User: {state.context.currentUser?.name}</p>
<button onClick={() => updateContext({ currentUser: { name: 'Soma' } })}>
Set Name to Soma
</button>
</div>
)
}
10. Resetting the Onboarding Flow
tsx
5 lines
function ResetButton() {
const { reset } = useOnboarding()
return <button onClick={() => reset()}>Restart Onboarding</button>
}
More Recipes
More recipes are coming soon! If you have specific use cases or patterns you'd like to see, please open an issue on our GitHub repository.