Path
Shows a record's stages as chevrons, with the current one and an action.
Installation
The CLI installs the Radix or Base UI file to match your project.
If your project doesn't have Wisty UI's theme yet, add it once with npx shadcn@latest add @wisty/cosmos. Until then, components keep shadcn's default look. Installation has the details.
When to use
Use it
- A record moves through a fixed process, like a lead's status or an opportunity's stage, and people need to see where it is and move it on. It usually sits right under the record page's header.
- People should be able to look at another stage before they move the record there.
- A process ends as won or lost.
Don't use it
- For steps someone completes in a form or setup flow. Use Progress indicator.
- For switching between views of content. Use Tabs.
- For a percentage. Use Progress.
- For more stages than fit across the page. Path has no scroll controls yet. Keep it to about seven stages.
Usage
Give Path its stages in order, the current one and an aria-label, with the action, usually a Button, as its children.
import { Button } from "@/components/ui/button"
import { Path } from "@/components/wisty/path"<Path
aria-label="Opportunity stage"
steps={[
{ value: "prospecting", label: "Prospecting" },
{ value: "qualification", label: "Qualification" },
{ value: "proposal", label: "Proposal" },
]}
current="qualification"
>
<Button>Mark Stage as Complete</Button>
</Path>Selecting a stage only looks at it. Your button's label follows the selection: "Mark as Current Stage" when another stage is selected, "Mark Status as Complete" when the current one is. The Lead status example shows both.
Examples
States
A later `current` stage, another stage selected with `defaultValue`, and paths closed as won and lost with `status`.
API reference
Path
| Prop | Type | Default | Description |
|---|---|---|---|
aria-label* | string | Names the stages for screen readers, like "Lead status". | |
children | React.ReactNode | The action area after the stages, usually a Button. | |
steps* | { value: string; label: string }[] | The stages, in order. | |
current* | string | The stage the record is at. Earlier stages show as complete. | |
value | string | The selected stage. Defaults to the current one. | |
defaultValue | string | ||
onValueChange | (value: string) => void | ||
status | "won" | "lost" | Closes the path at the current stage as won or lost. |
Also takes every <div> (opens in a new tab) prop.
SLDS mapping
Path is slds-path, the stages are slds-path__nav, and each stage is a slds-path__item with a slds-path__link inside. The action area is slds-path__action with slds-path__stage-name, and a Button in it is slds-path__mark-complete. The check is Lucide's where SLDS draws utility:check.
Known gaps:
- Coaching (
slds-path_has-coaching: key fields and guidance per stage, with a toggle) isn't covered. It will build on Tabs. - SLDS's scroll controls for a path wider than its container (
slds-has-overflow) aren't covered. Stages shrink to their minimum width, then the path clips. - SLDS's "Mark as Current Stage" button color (
slds-path__mark-current) isn't applied; use the brand Button.
| Prop | Value | SLDS | Notes |
|---|---|---|---|
current | (a step's value) (added) | slds-is-current, earlier steps slds-is-complete | |
value | (a step's value) (added) | slds-is-active, aria-selected | The selected stage. Defaults to current. |
status | won (added) | slds-is-won on the current step | |
status | lost (added) | slds-is-lost on the current step | Earlier steps stay incomplete, as in SLDS's example. |
children | (set) (added) | slds-path__action, slds-path__stage-name | The action area, usually slds-path__mark-complete. |
Keyboard interactions
| Key | Action |
|---|---|
| Right arrow | Moves focus and selection to the next stage. Stops at the last. |
| Left arrow | Moves focus and selection to the previous stage. Stops at the first. |
The stages are one tab stop, on the selected stage.
Accessibility
aria-labelnames the process for screen readers, like "Lead status" or "Opportunity stage". Name the process, not the control.- The stages are a horizontal listbox. Complete stages start with hidden text, "Stage Complete", and the current stage with "Current Stage:", so don't add state words of your own to a label.
Motion
- A complete stage's check flips to its name on hover, over 0.2s. With reduced motion it switches without the flip.
Responsive behavior
- The stages share the width equally, each at least 5rem. Long names are cut off with an ellipsis, so keep stage labels to a word or two.
- Below 64rem the action area moves under the stages and shows "Stage:" with the selected stage's name; below 48rem it stacks, with a full-width button.
Rules
- Change
currentfrom your own button, never on selection. - Use
statusonly whencurrentis the last stage, labelled "Closed Won" or "Closed Lost". - Put the Button directly in
Path's children. The action area sizes only a direct button; a wrapper around it loses that. - Never pass
asChildorrender.