Steps
Multi-step flows with derived statuses, error states and built-in wizard navigation.
- 1
Account
- 2
Shipping
- 3
Confirm
Email and password.
"use client";
import { useState } from "react";
import { Steps, Text, Box, HStack, VStack } from "astralis-ui";
export function StepsDemo() {
const [step, setStep] = useState(0);
return (
<VStack gap="6" alignItems="stretch" w="full" maxW="md">
<Steps step={step} onStepChange={setStep}>
<Steps.List>
<Steps.Item>
<Steps.Indicator />
<Steps.Title>Account</Steps.Title>
</Steps.Item>
<Steps.Item>
<Steps.Indicator />
<Steps.Title>Shipping</Steps.Title>
</Steps.Item>
<Steps.Item>
<Steps.Indicator />
<Steps.Title>Confirm</Steps.Title>
</Steps.Item>
</Steps.List>
<Box minH="16" pt="4">
<Steps.Content index={0}>
<Text size="sm" color="muted">Email and password.</Text>
</Steps.Content>
<Steps.Content index={1}>
<Text size="sm" color="muted">Where should we ship?</Text>
</Steps.Content>
<Steps.Content index={2}>
<Text size="sm" color="muted">Review your order.</Text>
</Steps.Content>
<Steps.Completed>
<Text size="sm" color="success" weight="medium">Order placed — all steps complete.</Text>
</Steps.Completed>
</Box>
<HStack gap="3">
<Steps.Prev />
<Steps.Next />
</HStack>
</Steps>
</VStack>
);
}Import#
import { Steps } from "astralis-ui";
// Steps.List, .Item, .Indicator, .Title, .Description,
// .Content, .Completed, .Prev, .NextUsage#
Each item's status — completed, active, upcoming, error — is derived from the
active index; indicators show a number, a check, or an error mark
accordingly. Steps.Prev/Steps.Next come pre-wired with end-of-flow
disabling, and Steps.Completed renders after the final step.
Variants#
Done
- 2
Active
- 3
Upcoming
Done
- 2
Active
- 3
Upcoming
Done
Active
Upcoming
"use client";
import { Steps, Text, VStack } from "astralis-ui";
const variants = ["solid", "subtle", "dot"] as const;
export function StepsVariants() {
return (
<VStack gap="6" alignItems="stretch" w="full" maxW="md">
{variants.map((variant) => (
<VStack key={variant} gap="2" alignItems="stretch">
<Text as="span" size="xs" color="muted">{variant}</Text>
<Steps variant={variant} defaultStep={1} size="sm">
<Steps.List>
<Steps.Item>
<Steps.Indicator />
<Steps.Title>Done</Steps.Title>
</Steps.Item>
<Steps.Item>
<Steps.Indicator />
<Steps.Title>Active</Steps.Title>
</Steps.Item>
<Steps.Item>
<Steps.Indicator />
<Steps.Title>Upcoming</Steps.Title>
</Steps.Item>
</Steps.List>
</Steps>
</VStack>
))}
</VStack>
);
}Vertical with descriptions#
Vertical orientation suits timelines and long flows; error on an item
flags it without breaking the sequence.
Repository created
astralis/astralis-ui
- !
CI failed
error marks a step red
- 3
Fix and re-run
You are here
- 4
Deploy
Upcoming
"use client";
import { Steps } from "astralis-ui";
export function StepsVertical() {
return (
<Steps orientation="vertical" defaultStep={2}>
<Steps.List>
<Steps.Item>
<Steps.Indicator />
<Steps.Title>Repository created</Steps.Title>
<Steps.Description>astralis/astralis-ui</Steps.Description>
</Steps.Item>
<Steps.Item error>
<Steps.Indicator />
<Steps.Title>CI failed</Steps.Title>
<Steps.Description>error marks a step red</Steps.Description>
</Steps.Item>
<Steps.Item>
<Steps.Indicator />
<Steps.Title>Fix and re-run</Steps.Title>
<Steps.Description>You are here</Steps.Description>
</Steps.Item>
<Steps.Item>
<Steps.Indicator />
<Steps.Title>Deploy</Steps.Title>
<Steps.Description>Upcoming</Steps.Description>
</Steps.Item>
</Steps.List>
</Steps>
);
}Clickable with bottom labels#
clickable turns indicators into jump buttons (linear restricts jumping
ahead); labelPlacement="bottom" centers titles under the track.
Cart
Payment
Receipt
"use client";
import { Steps, Box } from "astralis-ui";
export function StepsClickable() {
return (
/* clickable indicators jump straight to a step; labels sit beneath. */
<Box w="full" maxW="md">
<Steps clickable labelPlacement="bottom" defaultStep={1} variant="subtle">
<Steps.List>
<Steps.Item>
<Steps.Indicator />
<Steps.Title>Cart</Steps.Title>
</Steps.Item>
<Steps.Item>
<Steps.Indicator />
<Steps.Title>Payment</Steps.Title>
</Steps.Item>
<Steps.Item>
<Steps.Indicator />
<Steps.Title>Receipt</Steps.Title>
</Steps.Item>
</Steps.List>
</Steps>
</Box>
);
}Props#
| Prop | Type | Default | Description |
|---|---|---|---|
step · defaultStep · onStepChange | number · number · (step: number) => void | — · 0 · — | Controlled / initial step (0-based), and the change callback. |
variant | "solid" | "subtle" | "dot" | "solid" | Filled circles, outlined circles, or minimal dots. |
size | "sm" | "md" | "lg" | "md" | Indicator and typography scale together. |
orientation | "horizontal" | "vertical" | "horizontal" | Step layout direction. |
labelPlacement | "inline" | "bottom" | "inline" | Horizontal only — titles beside or centered beneath indicators. |
clickable | boolean | false | Indicators become buttons that jump to their step. |
linear | boolean | false | Restricts navigation to one step forward — no skipping ahead. |
count | number | — | Total steps; usually auto-counted from Steps.List children. |
Parts#
| Prop | Type | Default | Description |
|---|---|---|---|
Steps.Item | disabled?: boolean · error?: boolean | — | One step; status (completed/active/upcoming/error) is derived from the active index. |
Steps.Indicator | children?: ReactNode | — | Circle/dot — shows the number, a check when completed, or ! on error. Children override. |
Steps.Title / Steps.Description | children | — | Labels that recolor with the step's status. |
Steps.Content | index: number | — | Panel shown while its index is active. |
Steps.Completed | children | — | Shown once the flow advances past the last step. |
Steps.Prev / Steps.Next | children?: ReactNode · disabled?: boolean | "Back" / "Next" | Navigation buttons with automatic disabling at the ends. |
Accessibility#
- The list is a real
ol; the active indicator carriesaria-current="step". - Connectors are decorative (
aria-hidden); statuses are exposed as data attributes for styling. - Prev/Next are plain buttons — keyboard access comes for free.