Steps

A step progress tracker with optional navigation buttons, validation, and controlled or uncontrolled state.

Steps is a client component that renders a StepProgress track of numbered indicators and, optionally, previous/next buttons with a step counter. Use it for multi-step flows such as checkout or onboarding. Indicators animate with motion/react and show labels in Reactberry's Tooltip; no provider is required.

1
Cart
Shipping
Payment
Review
1 of 4

Import#

typescript
import { Steps, StepProgress, StepIndicator, StepsNav, useStepNavigation } from "@reactberry/system/blocks";
import type { StepConfig, StepThemeConfig, StepVariant, BaseStepProps } from "@reactberry/system/blocks";

Usage#

typescript
import { Steps, type StepConfig } from "@reactberry/system/blocks";
import { Box } from "@reactberry/system/elements";

const steps: StepConfig[] = [
  { label: "Account" },
  { label: "Profile" },
  { label: "Billing" },
  { label: "Review" },
];

export default function Onboarding() {
  return (
    <Box maxWidth="40rem" mx="auto" p="l">
      <Steps
        config={steps}
        showNavigation
        validateStep={(from, to) => to <= from + 1}
        onStepChange={(step) => console.log("Step", step)}
        onComplete={() => console.log("Done")}
      />
    </Box>
  );
}

Examples#

Controlled#

Set controlled and pass activeStep to drive the step from your own state. onStepChange receives the target step when an indicator is clicked.

Account
2
Profile
Billing
Review

Validation and completion#

validateStep cancels navigation when it returns false; here it blocks skipping ahead. navigationLabels replaces the button text, and onComplete runs when the finish button is pressed on the last step.

1
Workspace
Members
Integrations
Done
1 of 4
Steps can only move forward one at a time.

Compact track#

simple hides numbers and check icons; combine it with showLabels={false}, showTooltip={false} and showEdges={false} for a minimal progress bar. allowStepClick={false} makes the indicators read-only.

API#

Steps#

PropTypeDefaultDescription
initialStep
number
0

Initial active step (0-based)

controlled
boolean
false

Whether navigation is controlled externally

activeStep
number
—

Current active step when controlled

onStepChange
((step: number) => void)
—

Callback when step changes

showNavigation
boolean
false

Whether to show navigation buttons

navigationLabels
{ next?: string; previous?: string; finish?: string | undefined; } | undefined
{…

Custom labels for navigation buttons

allowStepClick
boolean
true

Whether steps can be clicked to navigate

validateStep
((fromStep: number, toStep: number) => boolean | Promise<boolean>)
—

Validation function for step navigation

loading
boolean
false

Loading state

onComplete
(() => void)
—

Callback when all steps are completed

StepsProps extends Omit<BaseStepProps, "active">, so it also takes config (StepConfig[], required) and variant (default "light"), skin, simple (default false), showLabels, showEdges, showTooltip (default true) and childProps, which are passed to StepProgress (see below). activeStep falls back to 0 when undefined. Passing navigationLabels replaces all three default labels. Remaining props are spread onto StepProgress, which spreads them onto its container Box.

StepProgress#

Renders a CSS grid with one StepIndicator per step, an optional pair of edge circles, and a progress track filled up to the active step. Steps before active render as completed.

PropTypeDefaultDescription
completed
number
—

Index of the completed step (all steps up to this index are completed)

config*
StepConfig[]
—

Array of step configurations

active*
number
—

Index of the currently active step (0-based)

skin
StepThemeConfig
—

Custom theme configuration

variant
"light""dark"
"light"

Theme variant to use

showLabels
boolean
true

Whether to show step labels

showEdges
boolean
true

Whether to show edge circles

showTooltip
boolean
true

Whether to show tooltips on hover

simple
boolean
false

Whether to use simple mode (no numbers/icons)

childProps
Record<string, any>
—

Additional props to pass to child components

setActive
((index: number) => void)
—

Callback when a step is clicked

skin replaces the built-in theme for variant entirely. childProps is spread onto every StepIndicator after the step's own config. completed is declared but not used; it is spread onto the container Box with the remaining props.

StepsNav#

A thin wrapper around StepProgress for externally managed state. All props other than mt and showLabels are passed to StepProgress.

PropTypeDefaultDescription
mt
string | number | object
medium

Additional margin top spacing

config*
StepConfig[]
—

Array of step configurations

active*
number
—

Index of the currently active step (0-based)

skin
StepThemeConfig
—

Custom theme configuration

variant
"light""dark"
—

Theme variant to use

showLabels
boolean
true

Whether to show step labels

showEdges
boolean
—

Whether to show edge circles

showTooltip
boolean
—

Whether to show tooltips on hover

simple
boolean
—

Whether to use simple mode (no numbers/icons)

childProps
Record<string, any>
—

Additional props to pass to child components

setActive
((index: number) => void)
—

Callback when a step is clicked

StepIndicator#

Renders one step: a circular dot and an optional label, wrapped in Tooltip (showing label) when showTooltip is true.

PropTypeDefaultDescription
active*
boolean
—

Whether this step is currently active

index*
number
—

The step number (1-based)

label*
string
—

The label text for this step

completed*
boolean
—

Whether this step is completed

variant*
StepThemeConfig
—

Theme configuration

simple
boolean
false

Whether to use simple mode (no numbers/icons)

showLabels
boolean
true

Whether to show labels

showTooltip
boolean
true

Whether to show tooltips on hover

setActive
((index: number) => void)
—

Callback when step is clicked

variant is the resolved theme object, not a StepVariant string. active scales the dot up and shows the step number; completed shows a check icon. setActive is called with index - 1 on click and switches the cursor to pointer. Remaining props are spread onto the indicator Box.

useStepNavigation#

Parameters

PropTypeDefaultDescription
totalSteps*
number
—

Number of steps.

initialStep
number
0

Initial 0-based step.

Returns

PropTypeDefaultDescription
activeStep
number
—

Current 0-based step.

nextStep
() => void
—

Moves forward one step, clamped to totalSteps - 1.

prevStep
() => void
—

Moves back one step, clamped to 0.

goToStep
(step: number) => void
—

Sets the step if it is within range; otherwise does nothing.

isFirst
boolean
—

true when activeStep is 0.

isLast
boolean
—

true when activeStep is totalSteps - 1.

progress
number
—

((activeStep + 1) / totalSteps) * 100, or 0 when there are no steps.

setActiveStep
Dispatch<SetStateAction<number>>
—

The raw state setter, without clamping.

StepConfig#

PropTypeDefaultDescription
label*
string
—

Label and tooltip text.

path
string
—

Declared; not used.

canNavigate
boolean
—

Declared; not used.

[key: string]
any
—

Other keys are spread onto the StepIndicator.

StepThemeConfig#

All fields are required. StepVariant is "light" | "dark".

PropTypeDefaultDescription
baseColor*
string
—

Label colour of the active step.

textColor*
string
—

Label colour of other steps.

borderColor*
string
—

Border colour of the dots and edge circles.

trackColor*
string
—

Track background colour.

completedTrackColor*
string
—

Colour of the filled part of the track.

size*
string
—

Track height; dots are size * 8.

unit*
string
—

CSS unit for size.

fontSize*
string
—

Font size of numbers and labels.

presets*
{ active, completed, default: { bg: string; color: string } }
—

Dot colours per state.

Built-in themes: light uses an accent completed track and accent active/completed dots; dark uses a brand completed track, white active dots, and primary completed dots. Both use a 0.3rem track and fontSize "small".

Accessibility#

  • Navigation buttons are native button elements with type="button", so they are keyboard accessible and do not submit an enclosing form. loading sets disabled on both.
  • Step indicators are div elements with a click handler and no role, tabIndex or keyboard handling, so they can't be reached or activated with the keyboard.
  • The step counter is plain text ("N of M") with no live region.

Notes#

  • Each StepConfig object is spread onto its StepIndicator, so extra keys (including path and canNavigate) reach the indicator Box and the DOM. path and canNavigate are declared but not used; canNavigate: false does not block navigation.
  • In controlled mode, isFirst and isLast still come from the internal step state (seeded by initialStep), so the previous button visibility, the finish label, and onComplete do not follow activeStep. validateStep is not called for the next button in controlled mode.
  • A setActive prop passed to Steps is spread after the internal click handler and replaces it.
  • Without onComplete, pressing next on the last step keeps the step clamped but still calls onStepChange with config.length.
  • Labels are hidden on the smallest breakpoint (display of ["none", "flex"]).