Skip to main content
0.66.1
View Zag.js on Github
Join the Discord server

Steps

Steps are used to guide users through a series of steps in a process. It's a great way to break down a complex process into smaller, more manageable steps.

Step 1

Features

  • Supports horizontal and vertical orientations.
  • Support for changing the active step with the keyboard and pointer.
  • Support for linear and non-linear steps.

Installation

To use the steps machine in your project, run the following command in your command line:

npm install @zag-js/steps @zag-js/react # or yarn add @zag-js/steps @zag-js/react

This command will install the framework agnostic steps logic and the reactive utilities for your framework of choice.

Anatomy

To set up the steps correctly, you'll need to understand its anatomy and how we name its parts.

Each part includes a data-part attribute to help identify them in the DOM.

Usage

First, import the steps package into your project

import * as steps from "@zag-js/steps"

The steps package exports two key functions:

  • machine — The state machine logic for the steps widget.
  • connect — The function that translates the machine's state to JSX attributes and event handlers.

You'll also need to provide a unique id to the useMachine hook. This is used to ensure that every part has a unique identifier.

Next, import the required hooks and functions for your framework and use the steps machine in your project 🔥

import * as steps from "@zag-js/steps" import { useMachine, normalizeProps } from "@zag-js/react" import { useId } from "react" const stepsData = [ { title: "Step 1" }, { title: "Step 2" }, { title: "Step 3" }, ] function Steps() { const [state, send] = useMachine( steps.machine({ id: useId(), count: stepsData.length }), ) const api = steps.connect(state, send, normalizeProps) return ( <div {...api.getRootProps()}> <div {...api.getListProps()}> {stepsData.map((step, index) => ( <div key={index} {...api.getItemProps({ index })}> <button {...api.getTriggerProps({ index })}> <div {...api.getIndicatorProps({ index })}>{index + 1}</div> <span>{step.title}</span> </button> <div {...api.getSeparatorProps({ index })} /> </div> ))} </div> {stepsData.map((step, index) => ( <div key={index} {...api.getContentProps({ index })}> {step.title} </div> ))} <div {...api.getContentProps({ index: stepsData.length })}> Steps Complete - Thank you for filling out the form! </div> <div> <button {...api.getPrevTriggerProps()}>Back</button> <button {...api.getNextTriggerProps()}>Next</button> </div> </div> ) }

Setting the initial step

Set the initial step by passing the step property to the machine context.

The value of the step property is zero-based index.

const [state, send] = useMachine( steps.machine({ step: 1, }), )

Listening for step change

When the active step changes, the machine will invoke the onStepChange event

const [state, send] = useMachine( steps.machine({ onStepChange(details) { // details => { step: number } console.log(`Step changed to ${details.step}`) }, }), )

Listening for steps completion

When all steps are completed, the machine will invoke the onStepComplete event

const [state, send] = useMachine( steps.machine({ onStepComplete() { console.log("All steps are complete") }, }), )

Enforcing linear steps

To enforce linear steps, you can set the linear prop to true when creating the steps machine. This will prevent users from skipping steps.

const [state, send] = useMachine( steps.machine({ linear: true, }), )

Changing the orientation

The steps machine supports both horizontal and vertical orientations. You can set the orientation prop to horizontal or vertical to change the orientation of the steps.

const [state, send] = useMachine( steps.machine({ orientation: "vertical", }), )

Styling guide

Earlier, we mentioned that each steps part has a data-part attribute added to them to select and style them in the DOM.

[data-scope="steps"][data-part="root"] { /* styles for the root part */ } [data-scope="steps"][data-part="root"][data-orientation="horizontal|vertical"] { /* styles for the root part based on orientation */ } [data-scope="steps"][data-part="list"] { /* styles for the list part */ } [data-scope="steps"][data-part="list"][data-orientation="horizontal|vertical"] { /* styles for the list part based on orientation */ } [data-scope="steps"][data-part="separator"] { /* styles for the separator part */ } [data-scope="steps"][data-part="separator"][data-orientation="horizontal|vertical"] { /* styles for the separator part based on orientation */ }

Current step

To style the current step, you can use the data-current attribute.

[data-scope="steps"][data-part="item"][data-current] { /* item styles for the current step */ } [data-scope="steps"][data-part="separator"][data-current] { /* separator styles for the current step */ }

Completed step

To style the completed step, you can use the data-complete attribute.

[data-scope="steps"][data-part="item"][data-complete] { /* item styles for the completed step */ } [data-scope="steps"][data-part="separator"][data-complete] { /* separator styles for the completed step */ }

Incomplete step

To style the incomplete step, you can use the data-incomplete attribute.

[data-scope="steps"][data-part="item"][data-incomplete] { /* item styles for the incomplete step */ } [data-scope="steps"][data-part="separator"][data-incomplete] { /* separator styles for the incomplete step */ }

Methods and Properties

Machine Context

The steps machine exposes the following context properties:

  • idsElementIdsThe custom ids for the stepper elements
  • stepnumberThe current value of the stepper
  • onStepChange(details: StepChangeDetails) => voidCallback to be called when the value changes
  • onStepCompleteVoidFunctionCallback to be called when a step is completed
  • linearbooleanIf `true`, the stepper requires the user to complete the steps in order
  • orientation"horizontal" | "vertical"The orientation of the stepper
  • countnumberThe total number of steps
  • dir"ltr" | "rtl"The document's text/writing direction.
  • idstringThe unique identifier of the machine.
  • getRootNode() => ShadowRoot | Node | DocumentA root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.

Machine API

The steps api exposes the following methods:

  • valuenumberThe value of the stepper.
  • percentnumberThe percentage of the stepper.
  • countnumberThe total number of steps.
  • hasNextStepbooleanWhether the stepper has a next step.
  • hasPrevStepbooleanWhether the stepper has a previous step.
  • setValue(value: number) => voidFunction to set the value of the stepper.
  • goToNextStep() => voidFunction to go to the next step.
  • goToPrevStep() => voidFunction to go to the previous step.
  • resetStep() => voidFunction to go to reset the stepper.
  • getItemState(props: ItemProps) => ItemStateReturns the state of the item at the given index.

Data Attributes

Root
data-scope
steps
data-part
root
data-orientation
The orientation of the steps
List
data-scope
steps
data-part
list
data-orientation
The orientation of the list
Item
data-scope
steps
data-part
item
data-orientation
The orientation of the item
Trigger
data-scope
steps
data-part
trigger
data-state
"open" | "closed"
data-orientation
The orientation of the trigger
data-complete
Present when the trigger value is complete
data-current
Present when current
Content
data-scope
steps
data-part
content
data-state
"open" | "closed"
data-orientation
The orientation of the content
Indicator
data-scope
steps
data-part
indicator
data-complete
Present when the indicator value is complete
data-current
Present when current
Separator
data-scope
steps
data-part
separator
data-orientation
The orientation of the separator
data-complete
Present when the separator value is complete
data-current
Present when current
Progress
data-scope
steps
data-part
progress
data-complete
Present when the progress value is complete

Edit this page on GitHub

Proudly made in🇳🇬by Segun Adebayo

Copyright © 2024
On this page