Back to components
Content

Steps List

A structured checklist for setup, onboarding, and release flows.

Page preview
Source
This is the component source that shadcn installs into a consuming project.
registry/default/steps-list/steps-list.tsx
import { CheckIcon } from "lucide-react"

import { Badge } from "@/components/ui/badge"
import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"
import { Separator } from "@/components/ui/separator"

type StepListItem = {
  title: string
  description: string
}

type StepsListCopy = {
  badge: string
  title: string
  description: string
  doneLabel: string
  steps: [StepListItem, StepListItem, StepListItem]
}

type StepsListProps = {
  copy?: StepsListCopy
}

const defaultCopy: StepsListCopy = {
  badge: "Workflow",
  title: "Publish checklist",
  description: "Use this pattern for onboarding, release flow, or setup progress.",
  doneLabel: "Done",
  steps: [
    {
      title: "Design the block",
      description: "Compose existing primitives and keep the public props small.",
    },
    {
      title: "Build registry JSON",
      description: "Generate installable artifacts for every component item.",
    },
    {
      title: "Verify installation",
      description: "Install into a clean app before sharing the registry URL.",
    },
  ],
}

export function StepsList({ copy = defaultCopy }: StepsListProps) {
  return (
    <Card className="w-full max-w-xl">
      <CardHeader>
        <Badge className="w-fit" variant="secondary">
          {copy.badge}
        </Badge>
        <CardTitle>{copy.title}</CardTitle>
        <CardDescription>{copy.description}</CardDescription>
      </CardHeader>
      <CardContent className="flex flex-col gap-4">
        {copy.steps.map((step, index) => (
          <div key={step.title} className="flex flex-col gap-4">
            <div className="flex gap-3">
              <div className="flex size-7 shrink-0 items-center justify-center rounded-full border bg-background text-xs font-medium">
                {index + 1}
              </div>
              <div className="flex min-w-0 flex-1 flex-col gap-1">
                <div className="flex flex-wrap items-center gap-2">
                  <h3 className="text-sm font-medium">{step.title}</h3>
                  {index === 0 ? (
                    <Badge variant="outline">
                      <CheckIcon />
                      {copy.doneLabel}
                    </Badge>
                  ) : null}
                </div>
                <p className="text-sm leading-6 text-muted-foreground">
                  {step.description}
                </p>
              </div>
            </div>
            {index < copy.steps.length - 1 ? <Separator /> : null}
          </div>
        ))}
      </CardContent>
    </Card>
  )
}