Maud Benaddi

Setup Checklist

Dashboards

About

A get-started checklist that lands partly done, with a progress ring, encouragement and a celebration on completion.

Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds Setup Checklist to your project and fits it to your theme.

Code

How Setup Checklist is used: the demo's code. The component's own source comes with a free account.

Usage.tsx

import * as React from "react";
import { SetupChecklist } from "@/components/ui/setup-checklist";

/* Shipyard, a made-up deployment tool for developers. The checklist lands
   two steps in (the account exists, the CLI is installed), the Friday deploy
   fails once on purpose so the error state shows, and the last step opens the
   first real task. */

const settings = {
  accent: "#7f72e3",
  accentForeground: "#060408",
  title: "Set up Shipyard",
  subtitle: "Five steps to your first deploy. Two are already behind you.",
  step1Title: "Create your account",
  step1Description: "Done when you signed up. That was the easy part.",
  step1Cta: "Open account",
  step2Title: "Install the CLI",
  step2Description: "One command, and you can ship from the terminal like it's 2009.",
  step2Cta: "Copy the command",
  step3Title: "Connect your repo",
  step3Description: "Shipyard watches your main branch and builds every push.",
  step3Cta: "Connect GitHub",
  step4Title: "Invite a teammate",
  step4Description: "Someone has to review your PR. It can't be you, we checked.",
  step4Cta: "Send an invite",
  step5Title: "Deploy on a Friday",
  step5Description: "Optional, brave. We keep a rollback button warm, just in case.",
  step5Cta: "Deploy to production",
  message1: "Everyone starts at zero. Even the unicorns.",
  message2: "Warming up. The hardest commit is the first one.",
  message3: "Nice pace. You haven't even opened the docs.",
  message4: "Past halfway. Your standup update writes itself.",
  message5: "One left. Don't let it sit in review.",
  doneTitle: "You're all set",
  nextTaskEyebrow: "Your first real task",
  nextTaskTitle: "Ship your first preview",
  nextTaskDescription: "Push a branch and Shipyard gives it its own URL to share.",
  nextTaskCta: "Open my project",
};

/* a little tugboat: the Shipyard mark */
function Tug() {
  return (
    <svg width="40" height="40" viewBox="0 0 40 40" aria-hidden className="block">
      <rect width="40" height="40" rx="11" className="fill-muted" />
      <path d="M8 24h24l-3.2 6.2a2 2 0 0 1-1.8 1.1H13a2 2 0 0 1-1.8-1.1Z" fill="currentColor" />
      <rect x="13" y="15" width="12" height="9" rx="2" className="fill-foreground" />
      <rect x="20" y="9" width="3.5" height="6" rx="1" fill="currentColor" />
      <circle cx="17" cy="19.5" r="1.3" className="fill-background" />
      <circle cx="21.5" cy="19.5" r="1.3" className="fill-background" />
      <path d="M17.2 22q2.1 1.2 4.2 0" fill="none" strokeWidth="1" strokeLinecap="round" className="stroke-background" />
    </svg>
  );
}

const wait = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));

export default function Demo(props: Partial<typeof settings>) {
  const s = { ...settings, ...props };
  const fridayTries = React.useRef(0);

  const steps = [
    { title: s.step1Title, description: s.step1Description, cta: s.step1Cta, done: true },
    { title: s.step2Title, description: s.step2Description, cta: s.step2Cta, done: true },
    { title: s.step3Title, description: s.step3Description, cta: s.step3Cta, pendingLabel: "Connecting", onAction: () => wait(900) },
    { title: s.step4Title, description: s.step4Description, cta: s.step4Cta, pendingLabel: "Sending", onAction: () => wait(700) },
    {
      title: s.step5Title,
      description: s.step5Description,
      cta: s.step5Cta,
      optional: true,
      requires: 2,
      pendingLabel: "Deploying",
      onAction: async () => {
        await wait(1100);
        fridayTries.current += 1;
        if (fridayTries.current === 1) throw new Error("Build failed. It's Friday, of course it did.");
      },
    },
  ];

  return (
    <div className="flex w-full items-center justify-center p-4 sm:p-6">
      <SetupChecklist
        className="max-w-4xl"
        accent={s.accent}
        accentForeground={s.accentForeground}
        messages={[s.message1, s.message2, s.message3, s.message4, s.message5]}
        doneTitle={s.doneTitle}
        labels={{ complete: "Setup complete. Nothing left to tick." }}
        title={s.title}
        subtitle={s.subtitle}
        logo={<Tug />}
        steps={steps}
        resettable
        onComplete={() => {
          fridayTries.current = 0;
        }}
        nextTask={{ eyebrow: s.nextTaskEyebrow, title: s.nextTaskTitle, description: s.nextTaskDescription, cta: s.nextTaskCta }}
      />
    </div>
  );
}

Dependencies

More by Maud Benaddi

View all
  • Notched Project Card

    Notched Project Card

    257
  • Tool Mesh

    Tool Mesh

    29
  • Voice Orb

    Voice Orb

    1
  • MCP Hub

    MCP Hub

  • Transcript to Prototype

    Transcript to Prototype

  • Role Flip Cards

    Role Flip Cards

  • Workflow Pills

    Workflow Pills

More in Dashboards

View all
  • Dashboard with Collapsible Sidebar

    Dashboard with Collapsible Sidebar

    2.4K
  • Sidebar

    Sidebar

    234
  • SaaS Template

    SaaS Template

    2.4K
  • Stats Bento

    Stats Bento

    1.1K
  • Financial Bento Grid

    Financial Bento Grid

    71
  • Stats cards with links

    Stats cards with links

    33
  • Course Design Cards

    Course Design Cards

    1.1K
  • Chat template

    Chat template

    598