Onboarding screen where users select their role or persona to personalize their experience. Each role card shows an icon, title, and description with a selectable highlight state.
Single-select persona screen: a responsive grid of icon cards lets the user pick the role that best describes them, with the primary CTA disabled until a card is selected. Selection lives in local component state only; there is no callback prop to report which role was chosen.
Pro blocks install through the shadcn CLI with your license key and ship their full source. Docs and live previews stay open to everyone, so you can read every block's details first.
Swap YOUR_EMAIL and YOUR_KEY for the email and license key on your account. Find your license key on your account page.
Radix flavor
npx shadcn add "https://ui.beste.co/r/onboarding13?email=YOUR_EMAIL&license_key=YOUR_KEY"Base UI flavor
npx shadcn add "https://ui.beste.co/r-base/onboarding13?email=YOUR_EMAIL&license_key=YOUR_KEY"This installs the block to components/beste/block/onboarding13.tsx and the badge and button shadcn/ui primitives it depends on.
The installed file exports onboarding13Demo alongside the block: the exact props behind the preview above. Spread it to get a working role picker in one line.
import { Onboarding13, onboarding13Demo } from "@/components/beste/block/onboarding13";
export default function OnboardingPage() {
return <Onboarding13 {...onboarding13Demo} />;
}Then replace the demo with your own props. Written out, a trimmed setup looks like this:
import { Code2, Palette, Users } from "lucide-react";
import { Onboarding13 } from "@/components/beste/block/onboarding13";
export default function OnboardingPage() {
return (
<Onboarding13
badge={{ label: "Personalize", variant: "secondary" }}
heading="What best describes your role?"
description="This helps us tailor your dashboard to how you work."
roles={[
{ id: "developer", icon: <Code2 className="size-6" />, title: "Developer", description: "I write code" },
{ id: "designer", icon: <Palette className="size-6" />, title: "Designer", description: "I design experiences" },
{ id: "manager", icon: <Users className="size-6" />, title: "Manager", description: "I lead teams" },
]}
primaryButton={{ label: "Continue", href: "/dashboard" }}
secondaryButton={{ label: "Skip for now", href: "/dashboard" }}
currentStep={1}
totalSteps={3}
/>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
badge | { label: string; variant?: "default" | "secondary" | "outline" } | – | Small badge above the heading |
heading | string | – | Main headline |
description | string | – | Supporting copy below the heading |
roles | Role[] | [] | Selectable role cards |
primaryButton | { label: string; href: string } | – | CTA, disabled until a role is selected |
secondaryButton | { label: string; href: string } | – | Ghost CTA, always enabled |
labels | { stepOf?: string; selectedHint?: string } | – | Step-counter template and the hint shown after a role is picked |
currentStep | number | 1 | Display-only step number used in the step label |
totalSteps | number | 4 | Display-only total step count used in the step label |
className | string | – | Extra classes for the outer <section> |
type Role = {
id: string;
icon: React.ReactNode;
title: string;
description: string;
};useState<string | null> only: clicking a card sets selected to that role's id. There is no callback prop to report the choice to a parent, so nothing is persisted beyond the component's own render, and reloading the page loses the selection.disabled={!selected}) until a role is chosen. On click it performs a raw window.location.href = primaryButton.href navigation rather than rendering an anchor, so it bypasses client-side routing entirely; the secondary button, by contrast, is a normal next/link Link with asChild.labels?.stepOf with a hardcoded literal fallback inside the component body ("Step {current} of {total}"), not merged from the demo module's own labels object the way onboarding1's step label is. currentStep/totalSteps are purely cosmetic numbers with no relation to role-selection state.roles.length: two or fewer roles render as a centered flex row, three roles or any count that isn't a multiple of four use a 3-column grid, and multiples of four use a 4-column grid, all collapsing to fewer columns on narrow viewports.labels.selectedHint only renders once a role is selected, directly beneath the role grid.ring-1 ring-primary outline in addition to the tinted background.onboarding17
Onboarding screen where users pick their primary goal to receive a tailored onboarding path. Each goal card shows a recommended feature set. Perfect for multi-purpose platforms.
onboarding25
Onboarding screen for choosing theme mode (light, dark, system) and accent color. Perfect for apps that offer visual customization as part of the onboarding experience.
onboarding16
Onboarding screen with a grid of selectable interest tags and chips for personalizing content recommendations. Perfect for content platforms, newsletters, and social apps.
onboarding20
Onboarding screen with a grid of starter templates to choose from, each with a preview thumbnail, title, and description. Perfect for project creation flows in design tools and project management apps.
onboarding18
Onboarding screen with toggle switches for configuring notification channels and frequency. Perfect for apps that need granular notification consent during setup.
onboarding26
Onboarding screen with import options from other platforms, CSV upload, and manual entry. Perfect for SaaS migration flows that help users bring existing data into the platform.