Basic

Step-by-step guides to build horizontal, vertical, and custom steppers with minimal code.

Horizontal Stepper

A horizontal stepper shows steps in a row. Here’s how to build one from scratch.

Define your steps

Use defineStepper with one object per step. Each step needs a unique id; add any extra fields (e.g. title) for your UI.

import {  } from "@stepperize/react";

const { ,  } = (
  { : "account", : "Account" },
  { : "profile", : "Profile" },
  { : "done", : "Done" }
);

Wrap with Stepper.Root

Stepper.Root provides the stepper context (it uses Scoped internally) and passes the stepper to your render function. Use orientation="horizontal" (default) for a horizontal layout.

import React from "react";
import {  } from "@stepperize/react";

const {  } = (
  { : "account", : "Account" },
  { : "profile", : "Profile" },
  { : "done", : "Done" }
);

function () {
  return (
    <.>
      {({  }) => (
        <>
          {/* list and content go here */}
        </>
      )}
    </.>
  );
}

Render the step list

Map over stepper.state.all and render one Stepper.Item per step. Each item needs a Trigger (and optionally Title, Indicator, Separator).

import React from "react";
import {  } from "@stepperize/react";

const {  } = (
  { : "account", : "Account" },
  { : "profile", : "Profile" },
  { : "done", : "Done" }
);

function () {
  return (
    <.>
      {({  }) => (
        <.>
          {...(() => (
            <React. ={.}>
              <. ={.}>
                <.>
                  <. />
                  <.>{.}</.>
                </.>
                <. />
              </.>
            </React.>
          ))}
        </.>
      )}
    </.>
  );
}

Show content per step

Use Stepper.Content with a step prop so each panel is visible only when that step is current.

import React from "react";
import {  } from "@stepperize/react";

const {  } = (
  { : "account", : "Account" },
  { : "profile", : "Profile" },
  { : "done", : "Done" }
);

function () {
  return (
    <.>
      {({  }) => (
        <>
          <. ="account"><>Account form</></.>
          <. ="profile"><>Profile form</></.>
          <. ="done"><>All done!</></.>
        </>
      )}
    </.>
  );
}

Add Prev / Next buttons

Put Stepper.Prev and Stepper.Next inside Stepper.Actions. They call navigation.prev() and navigation.next() and are disabled on first/last step automatically.

Live preview

Account form

Full example

import React from "react";
import {  } from "@stepperize/react";

const {  } = (
  { : "account", : "Account" },
  { : "profile", : "Profile" },
  { : "done", : "Done" }
);

function ({  }: { : React. }) {
  return (
    <. ="border border-gray-300 bg-white px-2 py-1 text-sm data-[status=active]:bg-blue-100 dark:data-[status=active]:bg-blue-900/40">
      {}
    </.>
  );
}

export function () {
  return (
    <.>
      {({  }) => (
        <>
          <. ="m-0 flex list-none flex-wrap gap-2 p-0">
            {...(() => (
              <React. ={.}>
                <. ={.}>
                  <>
                    <. ={() => < {...}>{.}</>} />
                  </>
                </.>
                {. !== "done" && < ="self-center text-gray-400">→</>}
              </React.>
            ))}
          </.>
          < ="mt-4 min-h-12">
            <. ="account"><>Account form</></.>
            <. ="profile"><>Profile form</></.>
            <. ="done"><>All done!</></.>
          </>
          < ="mt-4 flex flex-wrap gap-2">
            {.. ? (
              < ="button" ={() => ..()}>
                Reset
              </>
            ) : (
              <>
                <.>Previous</.>
                <.>Next</.>
              </>
            )}
          </>
        </>
      )}
    </.>
  );
}

Vertical Stepper

Same structure as the horizontal stepper; only the orientation changes. Pass orientation="vertical" to Stepper.Root and Stepper.List.

Use vertical orientation

Pass orientation="vertical" to Stepper.Root and Stepper.List. Use a custom separator (e.g. a line) between items since Stepper.Separator supports vertical layout.

Live preview

Content for Step 1

Full example

import React from "react";
import {  } from "@stepperize/react";

const {  } = (
  { : "one", : "Step 1" },
  { : "two", : "Step 2" },
  { : "three", : "Step 3" }
);

function ({  }: { : React. }) {
  return (
    <. ="border border-gray-300 bg-white px-2 py-1 text-sm data-[status=active]:bg-blue-100 dark:data-[status=active]:bg-blue-900/40">
      {}
    </.>
  );
}

function () {
  return < ="ml-3 h-3 w-px bg-gray-300 dark:bg-gray-600" />;
}

export function () {
  return (
    <. ="vertical">
      {({  }) => (
        <>
          <.
            ="vertical"
            ="m-0 flex w-fit list-none flex-col items-stretch gap-0 p-0"
          >
            {...((, ) => (
              <React. ={.}>
                <. ={.}>
                  <>
                    <. ={() => < {...}>{.}</>} />
                  </>
                </.>
                { < ... - 1 && < />}
              </React.>
            ))}
          </.>
          < ="mt-4 min-h-10">
            {...(() => (
              <. ={.} ={.}>
                <>Content for {.}</>
              </.>
            ))}
          </>
          < ="mt-4 flex flex-wrap gap-2">
            {.. ? (
              < ="button" ={() => ..()}>
                Reset
              </>
            ) : (
              <>
                <.>Back</.>
                <.>Continue</.>
              </>
            )}
          </>
        </>
      )}
    </.>
  );
}

Custom step indicator

By default, Stepper.Indicator has no built-in content. You control what it shows via children or the render prop. For example, show the step index (1, 2, 3) using useStepItemContext() from the primitives entry.

Custom indicator with step index

import React from "react";
import {  } from "@stepperize/react";
import {  } from "@stepperize/react/primitives";

const {  } = (
  { : "a", : "First" },
  { : "b", : "Second" },
  { : "c", : "Third" }
);

function () {
  const  = ();
  return <>{. + 1}</>;
}

function () {
  return (
    <.>
      {({  }) => (
        <.>
          {...(() => (
            <React. ={.}>
              <. ={.}>
                <.>
                  <.>
                    < />
                  </.>
                  <.>{.}</.>
                </.>
                <. />
              </.>
            </React.>
          ))}
        </.>
      )}
    </.>
  );
}

Alternative: render prop on Indicator

You can also use the render prop to receive DOM props and render a custom element (e.g. for styling the “circle” later).

import React from "react";
import {  } from "@stepperize/react";
import {  } from "@stepperize/react/primitives";

const {  } = (
  { : "x", : "Step X" },
  { : "y", : "Step Y" }
);

function () {
  const  = ();
  return (
    <.
      ={() => (
        < {...}>{. + 1}</>
      )}
    />
  );
}

// Use inside Stepper.Item:
// <Stepper.Trigger>
//   <CustomIndicator />
//   <Stepper.Title>{step.title}</Stepper.Title>
// </Stepper.Trigger>

Live preview

First step content

Full example

import React from "react";
import {  } from "@stepperize/react";
import {  } from "@stepperize/react/primitives";

const {  } = (
  { : "a", : "First" },
  { : "b", : "Second" },
  { : "c", : "Third" }
);

function () {
  const  = ();
  return <>{. + 1}</>;
}

function ({  }: { : React. }) {
  return (
    <. ="rounded-full border border-gray-300 bg-white px-2 py-1 text-sm data-[status=active]:bg-blue-100 dark:data-[status=active]:bg-blue-900/40">
      {}
    </.>
  );
}

export function () {
  return (
    <.>
      {({  }) => (
        <>
          <. ="m-0 flex list-none flex-wrap gap-2 p-0">
            {...(() => (
              <React. ={.}>
                <. ={.}>
                  <>
                    <. ="inline-flex size-4 items-center justify-center rounded-full bg-inherit text-xs font-semibold">
                      < />
                    </.>
                    <. ={() => < {...}>{.}</>} />
                  </>
                </.>
                {. !== "c" && < ="self-center text-gray-400">→</>}
              </React.>
            ))}
          </.>
          < ="mt-4 min-h-10">
            <. ="a"><>First step content</></.>
            <. ="b"><>Second step content</></.>
            <. ="c"><>Third step content</></.>
          </>
          < ="mt-4 flex flex-wrap gap-2">
            {.. ? (
              < ="button" ={() => ..()}>
                Reset
              </>
            ) : (
              <>
                <.>Previous</.>
                <.>Next</.>
              </>
            )}
          </>
        </>
      )}
    </.>
  );
}

Usage tips

  • Use stepper.flow.switch({ stepId: () => <Content /> }) to render content per step, or Stepper.Content step="stepId" for a declarative panel per step.
  • Use stepper.navigation.next(), prev(), and goTo(id) to change steps; Stepper.Prev and Stepper.Next do this for you when used inside Stepper.Actions.
  • Add validation between steps with stepper.lifecycle.onBeforeTransition; return false (or a promise resolving to false) to cancel the transition.
  • Style with CSS or Tailwind using data-component, data-status, and data-orientation on the primitives.
Edit on GitHub

Last updated on

On this page