Onboarding

A step-by-step flow with a segmented progress bar and CSS-only step transitions

Step 1

A short description of step 1.

Installation

pnpm dlx shadcn@latest add @alexandrejoly/onboarding

Usage

import {  Onboarding,  OnboardingContent,  OnboardingDescription,  OnboardingFooter,  OnboardingHeader,  OnboardingIndicator,  OnboardingNav,  OnboardingNextButton,  OnboardingPrevButton,  OnboardingStep,  OnboardingTitle,} from "@/components/ui/onboarding";
<Onboarding onComplete={() => setOpen(false)} totalSteps={2}>  <OnboardingContent>    <OnboardingStep value={1}>      <OnboardingHeader>        <OnboardingTitle>Step 1</OnboardingTitle>        <OnboardingDescription>A short description of step 1.</OnboardingDescription>      </OnboardingHeader>    </OnboardingStep>    <OnboardingStep value={2}>      <OnboardingHeader>        <OnboardingTitle>Step 2</OnboardingTitle>        <OnboardingDescription>A short description of step 2.</OnboardingDescription>      </OnboardingHeader>    </OnboardingStep>  </OnboardingContent>  <OnboardingFooter>    <OnboardingIndicator />    <OnboardingNav>      <OnboardingPrevButton />      <OnboardingNextButton />    </OnboardingNav>  </OnboardingFooter></Onboarding>

Examples

useOnboarding

useOnboarding drives custom controls: a step counter, then a skip button that completes the flow and turns into a restart on the last step.

Step 1 of 3

Step 1

A short description of step 1.

Transition

The text slides in from the right on Continue and from the left on Previous, while the media crossfades. The steps share one grid cell, so the leaving step fades out as the next fades in. Classes alone run it: data-starting-style and data-ending-style set where a step starts and ends, and data-direction picks the side. With reduced motion, the text fades without sliding.

Step 1

A short description of step 1.

Accessibility

OnboardingIndicator is one progressbar read as “Step 2 of 3”; its segments are hidden from assistive tech, so nobody hears one bar per step.

Focus stays on the button that changed the step, and the new step is not announced. When a step carries content people must hear, move focus to its OnboardingTitle, given tabIndex={-1}, once the step renders.

A step you leave is inert while it animates out, so focus and screen readers reach only the current step.

Meets WCAG 2.2: 2.4.3 Focus Order (opens in a new tab) · 4.1.2 Name, Role, Value (opens in a new tab)

API reference

Onboarding

Holds the current step. After the first move, data-direction reads next or previous.

Takes every prop of div.

PropTypeDefault
defaultValue

The step to start on, counted from 1.

Default: 1

number1
onComplete

Called when OnboardingNextButton is pressed on the last step.

() => voidNone
totalSteps

How many steps there are. Known on the first render, so the server draws the right indicator and button.

numberNone

OnboardingContent

Wraps the steps.

Takes every prop of div.

OnboardingStep

One step, rendered while it is the current one. The step you leave stays, with data-ending-style, until its transitions and animations end; the step you reach has data-starting-style for one frame, or turns back from where it is if it was still leaving. Both carry data-direction, next or previous. With no transition, the steps swap at once.

Takes every prop of div.

PropType
value

Its position, counted from 1.

number

OnboardingMedia

Centers an image or illustration and takes the height of what you put in it.

Takes every prop of div.

OnboardingHeader

Groups the title and the description under the media.

Takes every prop of div.

OnboardingTitle

The step’s title.

Takes every prop of h2.

OnboardingDescription

The step’s description.

Takes every prop of p.

Stacks the indicator over the nav.

Takes every prop of div.

OnboardingIndicator

One progressbar announced as “Step 2 of 3”, drawn as one shadcn Progress per step, hidden from assistive tech. Its name defaults to “Onboarding progress”; pass aria-label to change it.

Takes every prop of div.

OnboardingNav

Spreads the previous and next buttons apart.

Takes every prop of div.

OnboardingPrevButton

Ghost button to the previous step, disabled on the first. It stays focusable while disabled, so focus is not lost when it brings the flow back to step 1. An onClick runs before the step changes.

Takes every prop of shadcn Button (opens in a new tab).

PropTypeDefault
children

The label.

Default: "Previous"

ReactNode"Previous"

OnboardingNextButton

Moves forward, or calls onComplete on the last step. An onClick runs before the step changes.

Takes every prop of shadcn Button (opens in a new tab).

PropTypeDefault
children

The label.

Default: "Continue"

ReactNode"Continue"
lastLabel

The label on the last step.

Default: "Got it"

ReactNode"Got it"

useOnboarding()

Reads and drives the flow from any descendant of Onboarding. setStep throws outside 1 to totalSteps.

Return valueType
currentStep, totalSteps

The current step and the step count.

number
canGoToNextStep, canGoToPrevStep

Whether a step exists after or before the current one.

boolean
goToNextStep, goToPrevStep, reset

Move one step, or back to the first.

() => void
setStep

Jump to a step.

Dispatch<SetStateAction<number>>

View as Markdown