ComponentsNavigation

Progress tracker

The steps of a guided task, with where you are and what is done. Job setup lists them down the left; CV out stacks them as numbered sections.

Playground

Change the controls to try each prop. The code under the preview updates as you go and lists only the props you changed; copy it into your page.

import { ProgressStep } from '@/ui/progress-tracker';<ProgressStep>  Ideal candidate</ProgressStep>

Side stepper

A 260px column left of the step's content: the task, what it is about and how long it takes, then a 36px row per step. Every step opens, done or not; press any of them. Done is a check on the success fill, the step on screen is its number in a brand ring, to do is a number in a hairline ring, and skipped says so on the right.

import { useState } from 'react';
import { ProgressStep, ProgressTracker } from '@/ui/progress-tracker';

const steps = ['Job description', 'Ideal candidate', 'Get candidates', 'Review applicants'];
const done = new Set(['Job description']);
const skipped = new Set(['Review applicants']);

// Every step opens, done or not: the order is a suggestion.
export default function ProgressTrackerStepper() {
  const [open, setOpen] = useState('Ideal candidate');
  return (
    <ProgressTracker
      label="Setup steps"
      title="Set up Senior Data Engineer"
      description="Halcyon Bank · about 5 minutes"
      footer="Saved as you go. Leave any time and you'll come back to this step."
    >
      {steps.map((step) => (
        <ProgressStep key={step} status={open === step ? 'current' : done.has(step) ? 'done' : skipped.has(step) ? 'skipped' : 'todo'} onClick={() => setOpen(step)}>
          {step}
        </ProgressStep>
      ))}
    </ProgressTracker>
  );
}

Live row

When every step is done or skipped, a Live row appears under the list, passed as after.

import { SparkleIcon } from '@phosphor-icons/react';
import { ProgressStep, ProgressTracker } from '@/ui/progress-tracker';

// Every step done or skipped: the Live row appears under the list.
export default function ProgressTrackerLive() {
  return (
    <ProgressTracker
      label="Setup steps"
      title="Set up Senior Data Engineer"
      description="Halcyon Bank · about 5 minutes"
      after={
        <ProgressStep status="current" icon={<SparkleIcon className="text-success" />}>
          Live
        </ProgressStep>
      }
    >
      <ProgressStep status="done">Job description</ProgressStep>
      <ProgressStep status="done">Ideal candidate</ProgressStep>
      <ProgressStep status="done">Get candidates</ProgressStep>
      <ProgressStep status="skipped">Review applicants</ProgressStep>
    </ProgressTracker>
  );
}

Numbered sections

Where Flo fills every step at once and you check any of them, as in CV out: each step is a section you can open, with its number and name, a one-line summary of what it holds, and its state. Use inside a dialog or sheet, when the steps are short and seen together.

Maya Chen, Sara Lund and Tom Lindqvist, from the shortlist for Senior Data Engineer.
import { SpinnerGapIcon } from '@phosphor-icons/react';
import { FloMark } from '@/ui/flo-mark';
import { Lozenge } from '@/ui/lozenge';
import { ProgressSection } from '@/ui/progress-tracker';

// Flo fills every step at once; open any of them to check it.
export default function ProgressTrackerSections() {
  return (
    <div className="w-full max-w-180">
      <ProgressSection
        number={1}
        title="Pick candidates"
        summary="3 of 5: Maya Chen, Sara Lund, Tom Lindqvist"
        state={
          <>
            <FloMark />
            <span className="text-text">Flo</span> · filled
          </>
        }
        defaultOpen
      >
        Maya Chen, Sara Lund and Tom Lindqvist, from the shortlist for Senior Data Engineer.
      </ProgressSection>
      <ProgressSection number={2} title="Format CV" summary="3 CVs in the agency style, contact details removed" state={<Lozenge appearance="warning">Edited by you</Lozenge>}>
        Three CVs in the Northbridge template.
      </ProgressSection>
      <ProgressSection
        number={3}
        title="Draft the email"
        summary="3 emails to Halcyon Bank"
        state={
          <>
            <SpinnerGapIcon aria-hidden className="size-3" />
            Flo is filling
          </>
        }
      >
        Flo is writing the emails.
      </ProgressSection>
      <ProgressSection number={4} title="Approve" summary="Nothing goes until you send it">
        Check each email, then send.
      </ProgressSection>
    </div>
  );
}

On this page