Skip to content

shadcn.io is an independent project built on shadcn/ui

Free

Onboarding Dialog for Solid

A four-step onboarding dialog that opens on Welcome aboard, with step dots, Skip, and Next.

UI library: Ark UI

Start Onboarding opens a muted panel with a large icon, the title Welcome aboard, and one line under it. Four dots mark the steps, and the first is filled. Skip and Next sit beside the dots. The last step replaces them with Get Started.

Use it for a short first-run tour people can skip. If a step must point at the real screen, anchor a Popover there. In Solid, Ark UI does not remember that the tour finished.

Preview

Preview width

Installation

MCP · Pro
Make your AI a shadcn expert
Your agent installs components, builds pages and restyles your app from one prompt, in Claude Code, Cursor or VS Code.
Part of Pro

Anatomy

Dialog anatomy: content, title, description, close, footer

Usage

<Dialog onOpenChange={handleOpenChange}>
      <DialogTrigger asChild={props => <Button {...props()} variant="outline">Start Onboarding</Button>} />
      <DialogContent class="sm:max-w-2xl">
        <div class="flex h-64 items-center justify-center rounded-lg bg-muted">
          <Dynamic component={step().icon} aria-hidden="true" class="size-24 text-muted-foreground" />
        </div>
        <DialogHeader class="items-center text-center sm:text-center">
          <DialogTitle class="text-2xl">{step().title}</DialogTitle>
          <DialogDescription>{step().description}</DialogDescription>
        </DialogHeader>
        <div class="flex items-center justify-between">
          <div aria-hidden="true" class="flex gap-2">
            {steps.map((_item, index) => <div class={cn("size-2 rounded-full transition-colors", index === currentStep() ? "bg-foreground" : "bg-muted")} />)}
          </div>
          <div class="flex gap-4">
            {currentStep() < lastStep ? <>
                <Button onClick={handleSkip} variant="ghost">
                  Skip
                </Button>
                <Button onClick={handleNext}>
                  Next
                  <ArrowRightIcon aria-hidden="true" data-icon="inline-end" />
                </Button>
              </> : <DialogClose asChild={props => <Button {...props()}>
                  Get Started
                  <ArrowRightIcon aria-hidden="true" data-icon="inline-end" />
                </Button>} />}
          </div>
        </div>
      </DialogContent>
    </Dialog>

FAQ

How many steps should onboarding have?

Three to five, one idea each. Measure where people drop off and cut the steps nobody needs.

Should people be able to skip?

Always. Skip jumps to the last step here, and the close button and Escape close the dialog at any point.

How do the progress dots work?

They're mapped from the steps, with bg-foreground on the current one and bg-muted on the rest. They're decorative and aria-hidden; the title changes with each step.

Is each step announced?

Each step's title and text are the DialogTitle and DialogDescription, so the dialog is always labelled by the current step.

What should the illustration show?

Your product, ideally: a screenshot or a short animation of the feature. The demo uses a large Lucide icon on bg-muted for each step.

What should Get Started do?

It closes the dialog, and closing resets the steps for the next time. Point it at the first useful action in your app. Pass open and update it in onOpenChange, which receives { open }.

API Reference

Dialog

Sets onOpenChange={handleOpenChange}, asChild and props. The rest is on the Dialog page.

Button

Sets props, variant="outline", onClick={handleSkip}, variant="ghost" and onClick={handleNext}. The rest is on the Button page.