Skip to content
Destyler UI Destyler UI Destyler UI

Steps

Step 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 step.

Step 1
Step 1
Step 1
Step 1

Features

Install

Install the component from your command line.

Terminal window
      
        
npm install @destyler/steps @destyler/vue
Terminal window
      
        
npm install @destyler/steps @destyler/react
Terminal window
      
        
npm install @destyler/steps @destyler/svelte
Terminal window
      
        
npm install @destyler/steps @destyler/solid

Anatomy

Import all parts and piece them together.

<script setup lang="ts">
import * as steps from '@destyler/steps'
import { normalizeProps, useMachine } from '@destyler/vue'
import { computed, useId } from 'vue'
const stepsData = [
{ title: 'Step 1' },
{ title: 'Step 2' },
{ title: 'Step 3' },
]
const [state, send] = useMachine(
steps.machine({
id: useId(),
count: stepsData.length,
}),
)
const api = computed(() => steps.connect(state.value, send, normalizeProps))
</script>
<template>
<div v-bind="api.getRootProps()">
<div v-bind="api.getListProps()">
<div
v-for="(step, index) in stepsData"
:key="index"
v-bind="api.getItemProps({ index })"
>
<button v-bind="api.getTriggerProps({ index })">
<div v-bind="api.getIndicatorProps({ index })"></div>
</button>
<div v-bind="api.getSeparatorProps({ index })" />
</div>
</div>
<div
v-for="(step, index) in stepsData"
:key="index"
v-bind="api.getContentProps({ index })"
>
{{ step.title }}
</div>
<div v-bind="api.getContentProps({ index: stepsData.length })"></div>
<button v-bind="api.getPrevTriggerProps()"></button>
<button v-bind="api.getNextTriggerProps()"></button>
</div>
</template>
import { normalizeProps, useMachine } from '@destyler/react'
import * as steps from '@destyler/steps'
import { useId } from 'react'
const stepsData = [
{ title: 'Step 1' },
{ title: 'Step 2' },
{ title: 'Step 3' },
]
export default 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 })}></div>
</button>
<div {...api.getSeparatorProps({ index })}/>
</div>
))}
</div>
{stepsData.map((step, index) => (
<div key={index} {...api.getContentProps({ index })}></div>
))}
<div {...api.getContentProps({ index: stepsData.length })}></div>
<button {...api.getPrevTriggerProps()}></button>
<button {...api.getNextTriggerProps()}></button>
</div>
)
}
<script lang="ts">
import * as steps from '@destyler/steps'
import { normalizeProps, useMachine } from '@destyler/svelte'
const stepsData = [
{ title: 'Step 1' },
{ title: 'Step 2' },
{ title: 'Step 3' },
]
const [state, send] = useMachine(
steps.machine({
id: crypto.randomUUID(),
count: stepsData.length,
}),
)
const api = $derived(steps.connect(state, send, normalizeProps))
</script>
<div {...api.getRootProps()}>
<div {...api.getListProps()}>
{#each stepsData as step, index}
<div {...api.getItemProps({ index })}>
<button {...api.getTriggerProps({ index })}>
<div {...api.getIndicatorProps({ index })} ></div>
</button>
<div {...api.getSeparatorProps({ index })}></div>
</div>
{/each}
</div>
{#each stepsData as step, index}
<div {...api.getContentProps({ index })}></div>
{/each}
<div {...api.getContentProps({ index: stepsData.length })}></div>
<button {...api.getPrevTriggerProps()}></button>
<button {...api.getNextTriggerProps()}></button>
</div>
import { normalizeProps, useMachine } from '@destyler/solid'
import * as steps from '@destyler/steps'
import { createMemo, createUniqueId } from 'solid-js'
const stepsData = [
{ title: 'Step 1' },
{ title: 'Step 2' },
{ title: 'Step 3' },
]
export default function Steps() {
const [state, send] = useMachine(
steps.machine({
id: createUniqueId(),
count: stepsData.length,
}),
)
const api = createMemo(() => steps.connect(state, send, normalizeProps))
return (
<div {...api().getRootProps()} >
<div {...api().getListProps()} >
{stepsData.map((step, index) => (
<div {...api().getItemProps({ index })}>
<button {...api().getTriggerProps({ index })} >
<div {...api().getIndicatorProps({ index })}></div>
</button>
<div {...api().getSeparatorProps({ index })}/>
</div>
))}
</div>
{stepsData.map((step, index) => (
<div {...api().getContentProps({ index })}></div>
))}
<div
{...api().getContentProps({ index: stepsData.length })}
>
</div>
<button {...api().getPrevTriggerProps()}></button>
<button {...api().getNextTriggerProps()}></button>
</div>
)
}

Setting the initial step

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

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(`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 splitter 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"] {
/* 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"] {
/* 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"] {
/* 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:

ids
ElementIds
The custom ids for the stepper elements
step
number
The current value of the stepper
onStepChange
(details: StepChangeDetails) => void
Callback to be called when the value changes
onStepComplete
VoidFunction
Callback to be called when a step is completed
linear
boolean
If `true`, the stepper requires the user to complete the steps in order
orientation
"horizontal" | "vertical"
The orientation of the stepper
count
number
The total number of steps
dir
"ltr" | "rtl"
The document's text/writing direction.
id
string
The unique identifier of the machine.
getRootNode
() => ShadowRoot | Node | Document
A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.

Machine API

The steps api exposes the following methods:

value
number
The value of the stepper.
percent
number
The percentage of the stepper.
count
number
The total number of steps.
hasNextStep
boolean
Whether the stepper has a next step.
hasPrevStep
boolean
Whether the stepper has a previous step.
isCompleted
boolean
Whether the stepper is completed.
setStep
(step: number) => void
Function to set the value of the stepper.
goToNextStep
() => void
Function to go to the next step.
goToPrevStep
() => void
Function to go to the previous step.
resetStep
() => void
Function to go to reset the stepper.
getItemState
(props: ItemProps) => ItemState
Returns the state of the item at the given index.

Data Attributes

Root

name
desc
data-scope
steps
data-part
root
data-orientation
The orientation of the steps

List

name
desc
data-scope
steps
data-part
list
data-orientation
The orientation of the list

Item

name
desc
data-scope
steps
data-part
item
data-orientation
The orientation of the item

Trigger

name
desc
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

name
desc
data-scope
steps
data-part
content
data-state
"open" | "closed"
data-orientation
The orientation of the content

Indicator

name
desc
data-scope
steps
data-part
indicator
data-complete
Present when the indicator value is complete
data-current
Present when current

Separator

name
desc
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

name
desc
data-scope
steps
data-part
progress
data-complete
Present when the progress value is complete