User onboarding checklist UI
Help new users complete setup
A new user signs up. They have an account, but they haven't:
- Verified email.
- Enabled MFA.
- Added profile photo.
- Created their first resource.
A checklist UI shepherds them through. Increases retention.
Pattern
export function OnboardingChecklist({ user }) {
const tasks = [
{ id: "verify_email", label: "Verify your email", done: user.email_verified, href: "/verify-email" },
{ id: "set_name", label: "Add your name", done: user.has_name, href: "/settings" },
{ id: "enable_mfa", label: "Enable 2-factor authentication", done: user.has_mfa, href: "/settings/security" },
{ id: "first_task", label: "Create your first project", done: user.has_project, href: "/projects/new" },
{ id: "invite", label: "Invite a teammate (optional)", done: user.has_invites, href: "/team/invite", optional: true },
];
const completed = tasks.filter(t => t.done).length;
const total = tasks.length;
if (completed === total) return null; // hide once done
return (
<Card>
<CardHeader>
<h2>Get started ({completed}/{total})</h2>
<ProgressBar value={completed / total} />
</CardHeader>
<ul>
{tasks.map(t => (
<li key={t.id}>
<Checkbox checked={t.done} disabled />
<a href={t.href}>{t.label}</a>
{t.optional && <Badge>optional</Badge>}
</li>
))}
</ul>
</Card>
);
}Display on the main dashboard until complete.
What to include
Default checklist:
- Verify email (always).
- Add display name / photo (personalizes).
- Enable MFA (security).
- First domain action (varies by app).
- Invite teammates / set up integration (varies).
5-7 items. More is overwhelming.
Dismissal
Allow:
<Button variant="ghost" onClick={dismiss}>I'll do this later</Button>async function dismiss() {
await kratos.adminPatch(userId, [
{ op: "add", path: "/metadata_public/onboarding_dismissed", value: new Date().toISOString() }
]);
}Hide for 30 days, then re-show if still incomplete (re-engagement).
Confetti / celebration
When user completes a step:
useEffect(() => {
if (prevCompleted < completed) {
confetti();
track("onboarding_step_completed", { step: lastCompletedStep });
}
}, [completed]);Small reward. Motivates next step.
When all complete:
{completed === total && (
<Banner>
🎉 You're all set up! Here's a quick tour to help you start.
<Button>Take the tour</Button>
</Banner>
)}Track conversion
Funnel:
- Signed up.
- Verified email.
- Enabled MFA.
- Created first resource.
- Active 7 days.
Drop-off at each step tells you where users get stuck. Fix UX there.
Don't bombard
A 10-item checklist with tooltips and notifications is overwhelming. Stick to essentials. Trust users to discover advanced features.
Drip emails
Pair with email reminders. If user hasn't verified email after 3 days:
Subject: Quick reminder to verify your email
Hi,
You signed up 3 days ago but haven't verified your email yet.
Verifying takes 5 seconds:
[Resend verification email]
Once verified, you'll be able to:
- Recover your password if lost.
- Receive important notifications.After 7 days: another reminder. After 30: low-frequency or stop.
Don't spam.
Per-role checklists
If you have user types (free vs paid, admin vs member):
const tasks = baseTasks;
if (user.role === "admin") tasks.push({ id: "configure_team", ... });
if (user.plan === "free") tasks.push({ id: "explore_premium", ... });Customize per their context.
A/B testing
Different orderings, copy variations:
const variant = hashToBucket(user.id, 2);
const order = variant === 0 ? ["verify_email", "set_name", "enable_mfa", ...]
: ["enable_mfa", "verify_email", "set_name", ...];Test which order converts better.
Mobile
On mobile, the checklist might take whole screen. Use a sticky bottom-bar instead:
<BottomBar>
Setup progress: {completed}/{total}
<Button onClick={openChecklist}>View</Button>
</BottomBar>Tap → modal with full list.
When to hide forever
if (user.created_at < 30 days ago && completed >= total - 1) hide();User is set up well. Stop nagging.