# Upload Stages

> Compact file-upload card with three accent-colored bar segments for uploading, processing and ready. The current segment pulses while its stage runs and holds still under reduced motion.

A shadcn/ui block from the [@01orbital](https://01orbital.dev/?utm_source=shadcn.exchange) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/01orbital/upload-stages).

- Page, with a live preview: https://shadcn.exchange/r/01orbital/upload-stages
- Screenshot: https://shadcn.exchange/shot/01orbital/upload-stages/light.png
- Registry item: https://01orbital.dev/r/upload-stages.json
- Categories: stepper
- Listed: 2026-11-01, source last changed 2026-11-01

## Install

```bash
npx shadcn@latest add https://shadcn.exchange/r/01orbital/upload-stages.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `@phosphor-icons/react`
- Registry items: `@01orbital/button`, `@01orbital/stepper`

## Files

### `blocks/stepper/upload-stages/index.tsx`

```tsx
"use client"

import { useState } from "react"
import { FileIcon } from "@phosphor-icons/react"

import {
  Stepper,
  StepperItem,
  StepperList,
  StepperSeparator,
  StepperTitle,
} from "@/components/stepper/stepper"
import { Button } from "@/components/ui/button"

// The copy: change text here, not in the markup.
const COPY = {
  stages: ["Uploading", "Processing", "Ready"],
  file: "launch-brief-v3.pdf",
  size: "2.4 MB",
  advance: "Advance stage",
  reset: "Reset",
} as const

// Three stages as bars in the accent ink. The current segment breathes
// while its stage runs; reduced motion stills it. Advance is here only so
// the states can be seen; a real upload drives the value.
export default function UploadStages() {
  const [stage, setStage] = useState(1)
  const last = COPY.stages.length - 1
  const running = stage < last
  return (
    <div className="mx-auto w-full max-w-sm rounded-xl border border-border bg-card p-4 text-card-foreground [--stepper-accent:oklch(0.52_0.12_158)]">
      <div className="flex items-center gap-3">
        <span className="flex size-9 items-center justify-center rounded-lg bg-muted text-muted-foreground">
          <FileIcon className="size-4" aria-hidden />
        </span>
        <div className="min-w-0 flex-1">
          <p className="truncate text-sm font-medium">{COPY.file}</p>
          <p className="text-xs text-muted-foreground">
            {COPY.size} · {COPY.stages[stage]}
          </p>
        </div>
      </div>
      <Stepper
        value={stage}
        variant="bars"
        rail="accent"
        className="mt-4 gap-0"
      >
        <StepperList>
          {COPY.stages.map((s, i) => (
            <StepperItem key={s} value={i}>
              <StepperSeparator
                className={
                  running
                    ? "data-[state=active]:animate-pulse motion-reduce:animate-none"
                    : undefined
                }
              />
              <StepperTitle className="sr-only">{s}</StepperTitle>
            </StepperItem>
          ))}
        </StepperList>
      </Stepper>
      <div className="mt-4 flex justify-end gap-2">
        <Button size="xs" variant="ghost" onClick={() => setStage(0)}>
          {COPY.reset}
        </Button>
        <Button
          size="xs"
          variant="outline"
          disabled={!running}
          onClick={() => setStage(stage + 1)}
        >
          {COPY.advance}
        </Button>
      </div>
    </div>
  )
}
```
