Steps

free

Steps track progress through a multi-stage flow like checkout or onboarding.

Preview
Dark
import Steps from '@/components/ui/Steps';

export default function UsageDemo() {
  return (
    <div className="w-full">
      <Steps current={1}>
        <Steps.Item title="Account" />
        <Steps.Item title="Shipping" />
        <Steps.Item title="Payment" />
        <Steps.Item title="Review" />
      </Steps>
    </div>
  );
}

Installation

Add this component with the NateUI CLI.

npx nateui@latest add Steps

Examples

Basic

Preview
Dark
import Steps from '@/components/ui/Steps';

export default function BasicDemo() {
  return (
    <div className="w-full">
      <Steps current={2}>
        <Steps.Item />
        <Steps.Item />
        <Steps.Item />
        <Steps.Item />
      </Steps>
    </div>
  );
}

Title

Preview
Dark
import Steps from '@/components/ui/Steps';

export default function TitleDemo() {
  return (
    <div className="w-full">
      <Steps current={2}>
        <Steps.Item title="Cart" />
        <Steps.Item title="Address" />
        <Steps.Item title="Delivery" />
        <Steps.Item title="Confirm" />
      </Steps>
    </div>
  );
}

Custom Icon

Preview
Dark
import Steps from '@/components/ui/Steps';
import Spinner from '@/components/ui/Spinner';
import { PiFileMagnifyingGlass, PiSealCheck, PiSignIn } from 'react-icons/pi'

export default function CustomIconDemo() {
  return (
    <div className="w-full">
      <Steps current={1}>
        <Steps.Item title="Sign in" customIcon={<PiSignIn />} />
        <Steps.Item title="In review" customIcon={<Spinner size={16} />} />
        <Steps.Item title="Documents" customIcon={<PiFileMagnifyingGlass />} />
        <Steps.Item title="Approved" customIcon={<PiSealCheck />} />
      </Steps>
    </div>
  );
}

Error

Preview
Dark
import Steps from '@/components/ui/Steps';

export default function ErrorDemo() {
  return (
    <div className="w-full">
      <Steps current={2} status="error">
        <Steps.Item title="Details" />
        <Steps.Item title="Payment" />
        <Steps.Item title="Review" />
        <Steps.Item title="Done" />
      </Steps>
    </div>
  );
}

Vertical

Preview
Dark
import Steps from '@/components/ui/Steps';

export default function VerticalDemo() {
  return (
    <Steps vertical current={1}>
      <Steps.Item title="Account" />
      <Steps.Item title="Shipping" />
      <Steps.Item title="Payment" />
      <Steps.Item title="Review" />
    </Steps>
  );
}

Description

Preview
Dark
import Steps from '@/components/ui/Steps';

export default function DescriptionDemo() {
  return (
    <Steps vertical current={1}>
      <Steps.Item
        title="Account"
        description="Create your workspace login"
      />
      <Steps.Item
        title="Team"
        description="Invite the people you work with"
      />
      <Steps.Item
        title="Billing"
        description="Add a payment method"
      />
      <Steps.Item
        title="Launch"
        description="Publish your first project"
      />
    </Steps>
  );
}

Controlled

Preview
Dark
Step 1 of 4Account
import { useState } from 'react';
import Steps from '@/components/ui/Steps';
import Button from '@/components/ui/Button';

const LABELS = ['Account', 'Shipping', 'Payment', 'Review'];

export default function ControlledDemo() {
  const [current, setCurrent] = useState(0);
  const last = LABELS.length - 1;

  const goTo = (next: number) => {
    setCurrent(Math.min(Math.max(next, 0), last));
  };

  return (
    <div className="grid w-full gap-8">
      <Steps current={current} onChange={goTo}>
        {LABELS.map((label) => (
          <Steps.Item key={label} title={label} />
        ))}
      </Steps>
      <div className="flex items-center justify-between">
        <span className="text-sm text-muted-foreground">
          Step {current + 1} of {LABELS.length} — {LABELS[current]}
        </span>
        <div className="flex gap-2">
          <Button
            disabled={current === 0}
            onClick={() => goTo(current - 1)}
          >
            Previous
          </Button>
          <Button
            variant="solid"
            disabled={current === last}
            onClick={() => goTo(current + 1)}
          >
            Next
          </Button>
        </div>
      </div>
    </div>
  );
}

API

Steps is a compound component. Steps owns the current index and derives each item's status from it, while Steps.Item renders one stage and can override its own status, icon, title, and description.

Steps

PropDescriptionTypeDefault
currentZero-based index of the active step. Earlier steps render as complete, later steps as pending.number0
statusStatus applied to the active step. Use 'error' to flag a failed step in the flow.StepStatus'in-progress'
verticalStack steps vertically instead of in a horizontal row.booleanfalse
onChangeFired with a step index when a step is clicked. Providing it makes steps clickable.(index: number) => void-
classNameExtra classes for the steps container.string-

Steps.Item

PropDescriptionTypeDefault
titleStep label shown beside or below the indicator.string | ReactNode-
descriptionSupporting text under the title. Renders only when Steps is vertical.string | ReactNode-
customIconNode rendered inside the indicator in place of the check, cross, or step number.string | ReactNode-
statusOverrides the status derived from current for this step.StepStatus-
classNameExtra classes for the step item.string-

Types

type StepStatus = 'complete' | 'pending' | 'in-progress' | 'error'