Stepper

Import
import { Stepper } from "reshaped";
import type { StepperProps } from "reshaped";
Related components
Row and column direction
Supports collapsible item content


Stepper is a compound component and handles Stepper.Item components passed as its children. By default it displays all steps in a horizontal layout. Each item supports rendering title and subtitle which can be strings or React nodes.

<Stepper>
  <Stepper.Item title="Step 1" subtitle="Step 1 subtitle" />
  <Stepper.Item title="Step 2" />
  <Stepper.Item title="Step 3" />
</Stepper>

You can change the direction of the Stepper to column to display it vertically:

<Stepper direction="column">
  <Stepper.Item title="Step 1" subtitle="Step 1 subtitle" />
  <Stepper.Item title="Step 2" />
  <Stepper.Item title="Step 3" />
</Stepper>

Each item has a completed flag which replaces its number with a checkmark.

Additionally, you can control which step is currently active with an activeId prop. By default its using the index of the item you want to activate, e.g. if you want to make the first item active – you can pass an activeId={0}.

In case you're rendering items conditionally, you might want to rely on specific ids instead. Instead of using an index, you can pass any string as activeId to the Stepper and an id property to each of the items. Items with matching ids will be rendered as active.

<Stepper activeId={1}>
  <Stepper.Item title="Step 1" subtitle="Step 1 subtitle" completed />
  <Stepper.Item title="Step 2" />
  <Stepper.Item title="Step 3" />
</Stepper>

With the children suppport in the column layout – you can handle the user navigation through the steps. You can check a following example, where we have two buttons to navigate back and forward in between the steps.

function Demo(props) {
  const [activeId, setActiveId] = React.useState(1);

  const content = (
    <View gap={3}>
      <Placeholder />
      <View direction="row" gap={3}>
        <Button onClick={() => setActiveId((prev) => Math.max(0, prev - 1))}>
          Previous
        </Button>
        <Button onClick={() => setActiveId((prev) => Math.min(2, prev + 1))}>
          Next
        </Button>
      </View>
    </View>
  );

  return (
    <Stepper activeId={activeId} direction="column">
      <Stepper.Item
        completed={activeId > 0}
        title="Step 1"
        subtitle="Step subtitle"
      >
        {content}
      </Stepper.Item>
      <Stepper.Item completed={activeId > 1} title="Step 2">
        {content}
      </Stepper.Item>
      <Stepper.Item completed={activeId > 2} title="Step 3 very long title">
        {content}
      </Stepper.Item>
    </Stepper>
  );
}

Depending on where you render the Stepper and how much available space there is – you might need to hide the labels. You can control their rendering with the labelDisplay property, which you can use to always hide them or render them conditionally based on the viewport size. You can also compose the Stepper with other components in case you want to indicate the active step when the labels are hidden.

Compare how the following example adjusts its rendered content when switching between small and large viewports:

<View gap={2}>
  <Stepper activeId={1} labelDisplay={{ s: "hidden", m: "inline" }}>
    <Stepper.Item title="Step 1" subtitle="Step 1 subtitle" completed />
    <Stepper.Item title="Step 2" />
    <Stepper.Item title="Step 3" />
  </Stepper>

  <Hidden hide={{ s: false, m: true }}>
    <View direction="row" gap={4} justify="space-between">
      <Text weight="medium">Step 2</Text>
      <Text weight="medium">Step 2 of 3</Text>
    </View>
  </Hidden>
</View>