A reversed split with an image tile floating a live import dropzone on the left, and an eyebrow, light heading, three monospace-numbered steps, and an accent button on the right.
Two-column split layout with a floating live import dropzone over a background photo on one side, and an eyebrow, light heading, three monospace-numbered steps, and a CTA button on the other. Static content, the only "live" piece is the embedded dropzone component.
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/onboarding32?email=YOUR_EMAIL&license_key=YOUR_KEY"Base UI flavor
npx shadcn add "https://ui.beste.co/r-base/onboarding32?email=YOUR_EMAIL&license_key=YOUR_KEY"This installs the block to components/beste/block/onboarding32.tsx, the badge23 and button21 components it renders (the eyebrow pill and the accent CTA button), the upload32 piece embedded in the demo as the floating import dropzone, and its dependencies.
The installed file exports onboarding32Demo alongside the block: the exact props behind the preview above. Spread it to get a working data-import split in one line.
import { Onboarding32, onboarding32Demo } from "@/components/beste/block/onboarding32";
export default function OnboardingPage() {
return <Onboarding32 {...onboarding32Demo} />;
}Then replace the demo with your own props. Written out, a trimmed setup looks like this:
import { Onboarding32 } from "@/components/beste/block/onboarding32";
import { Upload32 } from "@/components/beste/piece/upload32";
export default function OnboardingPage() {
return (
<Onboarding32
badge={{ label: "Bring your data" }}
heading="Your records move over in one drop"
description="Export from whatever you use today, drop the file in, and we map the fields."
steps={[
{ title: "Export what you have", description: "A CSV from your old system." },
{ title: "Drop it in", description: "We detect the columns and match them." },
{ title: "Review and go live", description: "Confirm the preview and go." },
]}
button={{ label: "Start your import", href: "https://beste.co" }}
media={
<Upload32
title="Import member records"
hint="Drop a CSV or browse"
file={{ name: "members.csv", percent: 40 }}
/>
}
image={{
src: "https://images.unsplash.com/photo-1699391202824-247ed6fbc484?w=1200&h=900&fit=crop",
alt: "Deep green gradient backdrop",
}}
/>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
badge | Badge | – | Eyebrow pill above the heading |
heading | string | – | Right-column headline |
description | string | – | Right-column supporting copy |
steps | Step[] | [] | Ordered, numbered step list |
button | ActionLink | – | CTA rendered below the steps |
media | ReactNode | – | Content rendered inside the left media tile |
image | TileImage | – | Background photo behind media |
className | string | – | Extra classes for the outer <section> |
type Badge = { label: string };
type ActionLink = { label: string; href: string };
type Step = { title: string; description: string };
type TileImage = { src: string; alt: string };media is typed as a plain React.ReactNode, not the image | video | component discriminated union used elsewhere in this registry for media slots; the demo fills it with a live <Upload32 /> piece instance directly, so swapping the media means passing different JSX rather than a different data shape.image only renders when media is also set, both the background <img> and the media node live inside the same {media && (...)} conditional block, so passing only image without media renders neither the photo nor the dropzone column.media is omitted, the two-column grid (md:grid-cols-2) collapses to a single rendered child: the text column becomes the grid's only item and occupies the first track, leaving the second column blank on desktop instead of stretching to fill the row.image fills the tile edge-to-edge (absolute inset-0 object-cover) behind media, which is layered on top via relative z-10, so any live piece placed in media needs its own opaque chrome to stay legible over the photo.padStart(2, "0")), and the CTA (Button21) sits below the steps in the text column rather than beside the media tile.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.
onboarding31
A getting-started section with an eyebrow over a hairline rule, a two-column heading, three monospace-numbered steps under their own hairlines, and an accent button.
onboarding10
Interactive accordion layout where each step expands to reveal detailed instructions and action buttons. Perfect for setup guides that let users complete tasks at their own pace.
onboarding8
Interactive form wizard with segmented progress bar, form fields, and back/next navigation. Perfect for account registration flows collecting user and company information.
onboarding6
Vertical timeline layout with step indicators, descriptions, and a connecting progress line. Perfect for workspace setup flows with detailed step explanations.
onboarding5
Multi-step progress indicator with numbered circles connected by a progress line. Perfect for account setup wizards that show users their completion status.