An import section leading with an image tile that floats a live upload dropzone, then setting out the formats that are read and the records that come across as two checked hairline lists.
An import section leading with an image tile that floats a live upload dropzone, then setting out the formats that are read and the records that come across as two checked hairline lists.
Upgrade to Pro
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/onboarding38?email=YOUR_EMAIL&license_key=YOUR_KEY"Base UI flavor
npx shadcn add "https://ui.beste.co/r-base/onboarding38?email=YOUR_EMAIL&license_key=YOUR_KEY"This installs the block to components/beste/block/onboarding38.tsx, the upload32 dropzone piece it floats on the tile (installed to components/beste/piece/upload32.tsx), and the badge23 and button21 components it uses for the eyebrow and the actions.
The installed file exports onboarding38Demo alongside the block: the exact props behind the preview above. Spread it to get a working section in one line.
import { Onboarding38, onboarding38Demo } from "@/components/beste/block/onboarding38";
export default function ImportPage() {
return <Onboarding38 {...onboarding38Demo} />;
}Then replace the demo with your own props. Written out, a trimmed setup looks like this:
import { Onboarding38 } from "@/components/beste/block/onboarding38";
import { Upload32 } from "@/components/beste/piece/upload32";
export default function ImportPage() {
return (
<Onboarding38
badge={{ label: "Bringing your data" }}
heading="Send the messy export. That is the whole ask."
description="We have not met a format that stopped us."
media={
<Upload32
title="Import member records"
hint="Drop a CSV or browse, we map the fields"
file={{ name: "members_export_2026.csv", percent: 72 }}
/>
}
image={{ src: "/backdrops/blue.jpg", alt: "Soft blue gradient backdrop" }}
groups={[
{
title: "Formats we read",
items: ["CSV and TSV, any delimiter", "Excel workbooks, including multiple sheets"],
},
{
title: "What comes across",
items: ["Members, contacts, and reference numbers", "Appointment history"],
},
]}
reassurance="Anything we cannot map arrives as a note against the right member."
buttons={[
{ label: "Start an import", href: "/import" },
{ label: "Read the migration guide", href: "/docs/migration" },
]}
/>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
badge | { label: string } | – | Eyebrow above the heading, in the copy column |
heading | string | – | Section heading |
description | string | – | Supporting paragraph, capped at max-w-md |
media | ReactNode | – | Live asset on the tile, upload32 in the demo |
image | { src: string; alt: string } | – | Backdrop behind the media tile |
groups | FormatGroup[] | [] | Two checked lists, formats and records |
reassurance | string | – | What happens to data that cannot be mapped |
buttons | ActionLink[] | [] | Actions, first solid and the rest outlined |
className | string | – | Extra classes for the outer section |
type ActionLink = {
label: string;
href: string;
};
type TileImage = {
src: string;
alt: string;
};
type FormatGroup = {
title: string;
items: string[];
};order-first, so the dropzone leads on every screen size rather than only on mobile. The section is about making an import feel unthreatening, and the artefact does that better than the copy.groups is an array rather than fixed formats and records props, so a two-column pair is the tuned layout but three groups still reflow without breaking.aria-hidden and the text carries the meaning, so a screen reader hears a plain list.border-t with no closing rule, so both lists stay open at the bottom and flow into the reassurance line.reassurance sits outside the grid on its own, which keeps it reading as a caveat covering both lists rather than as part of either.lg:items-center, so the tile stays centered against a copy column whose height varies with the number of actions.onboarding32
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.
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.
onboarding41
A candid onboarding section listing what tends to go wrong on hairline rows, each with a tone-coded frequency chip, how often it has been seen, and the mitigation already in place.
onboarding33
A setup panel pairing a soft progress column, where an oversized percentage and accent bar react to what is ticked, with hairline checklist rows that each carry a description and a jump-in link.