Onboarding
A product's first minutes in one flow: set up a workspace, invite the team to it, then land in the app with a first-run checklist that already knows what was done.
4 Pro components · Eased Pro licence · every piece also works alone
The scene
The scene installs with every piece it uses.
The pieces
A setup flow that shows its result while you make it.
An invite flow whose result fills in while you type.
The checklist that lives in the product after setup.
One decision per screen, in a frame that keeps score.
Hand-offs
The flows share nothing but props. Workspace Setup's onInvite hands over the workspace it made, and Team Invite takes it as workspace; Team Invite's onDone reports what was sent, so the app ticks Invite your team with the checklist's complete. Each flow runs on useSteps from eased-onboarding-core and also works alone. Show one flow at a time: let the last one leave before the next enters, and swap at once under reduced motion.
export function Onboarding() { const [stage, setStage] = useState<"setup" | "invite" | "app">("setup"); const [workspace, setWorkspace] = useState<Workspace>(); const checklist = useFirstRunChecklist({ items: FIRST_RUN }); // Into the app: setup is done, and the invite is if one went out. const land = (w: Workspace, invited: boolean) => { setWorkspace(w); checklist.complete("workspace"); if (invited) checklist.complete("invite"); setStage("app"); }; if (stage === "setup") { return ( <WorkspaceSetup checkSlug={checkSlug} onCreate={createWorkspace} onInvite={(w) => { setWorkspace(w); setStage("invite"); }} onOpen={(w) => land(w, false)} /> ); } if (stage === "invite") { return <TeamInvite workspace={workspace} onSend={sendInvites} onDone={(sent) => land(workspace!, sent.invites.length > 0)} />; } return ( <App workspace={workspace}> <FirstRunChecklist checklist={checklist} onOpen={openPart} className="fixed bottom-4 end-4" /> </App> );}