Multi-step checklist with status indicators, ETAs, and a sticky sidebar showing completion progress and key stats. Perfect for onboarding flows, implementation guides, or setup wizards.
Two-column onboarding layout: a list of status-tagged setup steps on the left (each optionally a link, with an ETA and a done/in-progress/todo pill), and a sticky sidebar on the right showing a computed completion percentage, a progress bar, key stats, and CTA buttons.
Free block
This block is free. No license or account is required: install it with the CLI and use it in unlimited projects.
Radix flavor
npx shadcn add "https://ui.beste.co/r/feature22"Base UI flavor
npx shadcn add "https://ui.beste.co/r-base/feature22"This installs the block to components/beste/block/feature22.tsx and the shadcn/ui badge, button, and progress components it depends on.
The installed file exports feature22Demo alongside the block: the exact props behind the preview above. Spread it to get a working checklist in one line.
import { Feature22, feature22Demo } from "@/components/beste/block/feature22";
export default function Page() {
return <Feature22 {...feature22Demo} />;
}Then replace the demo with your own props. Written out, a trimmed setup looks like this:
import { Plug, ShieldCheck, Wand2 } from "lucide-react";
import { Feature22 } from "@/components/beste/block/feature22";
export default function Page() {
return (
<Feature22
badge={{ label: "Setup", variant: "secondary" }}
heading="Launch-ready in a single afternoon"
description="Configure essentials, validate data, and go live with confidence."
steps={[
{
id: "step-1",
icon: <Plug className="size-6 md:size-8" />,
title: "Connect your data sources",
eta: "5-10 min",
status: "done",
href: "/setup/connect",
},
{
id: "step-2",
icon: <ShieldCheck className="size-6 md:size-8" />,
title: "Configure roles & access",
eta: "10-15 min",
status: "in_progress",
},
{
id: "step-3",
icon: <Wand2 className="size-6 md:size-8" />,
title: "Apply templates & standards",
eta: "15-25 min",
status: "todo",
},
]}
stats={[{ id: "stat-1", label: "Avg. time to first publish", value: "45 min" }]}
buttons={[{ id: "btn-1", label: "Start a guided setup", href: "/setup" }]}
/>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
badge | { label: string; variant?: "default" | "secondary" | "outline" } | – | Badge above the heading |
heading | string | – | Section heading |
description | string | – | Section intro text |
progressLabel | string | "Progress" | Label above the sidebar's percentage readout |
stepsCompletedLabel | string | "steps completed" | Suffix after the done/total count in the sidebar |
steps | Step[] | [] | Checklist rows on the left |
stats | Stat[] | [] | Key-number rows inside the sidebar |
buttons | ButtonItem[] | [] | CTA buttons at the bottom of the sidebar |
className | string | – | Extra classes for the outer <section> |
type StepStatus = "done" | "in_progress" | "todo";
type Step = {
id: string;
icon?: React.ReactNode;
title: string;
description?: string;
eta?: string;
status?: StepStatus;
href?: string;
};
type Stat = {
id: string;
label: string;
value: string;
description?: string;
};
type ButtonItem = {
id: string;
label: string;
href?: string;
variant?: "default" | "secondary" | "outline" | "ghost" | "link" | "destructive";
};null when steps is empty (if (steps.length === 0) return null;), so an empty checklist renders nothing at all rather than an empty section shell, unlike the other blocks in this batch.useMemo, counting steps whose status is "done" against steps.length; the same progressPercent value drives both the sidebar's numeric readout and the Progress bar.href set renders as a full-width Link wrapping the whole card (hover:opacity-80 on hover); a step without href renders as a plain, non-interactive div with identical markup otherwise.lg:sticky lg:top-6, so it stays pinned in view while the step list scrolls past it on large screens; below lg the layout drops to a single column (the lg:grid-cols-[1.35fr_0.65fr] split only applies at lg and up) and the sidebar simply stacks after the steps.icon is typed as React.ReactNode, a pre-rendered element such as <Plug className="size-6 md:size-8" />, rather than the LucideIcon component-reference pattern (icon?: LucideIcon) used by the other blocks in this batch, so callers size and style the icon themselves before passing it in.feature60
Sticky sidebar navigation that scrolls to content sections with images and descriptions. Perfect for product feature tours, documentation pages, and guided walkthroughs.
feature119
Responsive grid of goal cards with percentage progress bars and labels. Excellent for OKR tracking, quarterly goals, or progress visualization.
feature164
Sticky sidebar navigation with scroll-spy tracking. Left menu highlights the active section as users scroll through feature cards with images. Uses IntersectionObserver for real-time active state.
feature158
Vertical stacked notification-style list with items, descriptions, and status badges. Great for updates feed, changelog, or activity log display.
feature146
Responsive grid of benefit items with checkmark icons and descriptions. Perfect for benefits list, value proposition, or key advantages.
feature116
2-column grid of checklist items with checkbox icons and strikethrough styling. Great for progress tracking, launch checklists, or task lists.