Steps
Used to guide users through a series of steps in a process
Usage
The Steps component is used to guide users through a series of steps in a process.
- Supports horizontal and vertical orientations.
- Support for changing the active step with the keyboard and pointer.
- Support for linear and non-linear steps.
import { Steps } from '@ark-ui/react/steps'
Examples
Basic
Here's a basic example of the Steps component.
import { Steps } from '@ark-ui/react/steps'
const items = [
{ value: 'first', title: 'First', description: 'Contact Info' },
{ value: 'second', title: 'Second', description: 'Date & Time' },
{ value: 'third', title: 'Third', description: 'Select Rooms' },
]
export const Basic = () => {
return (
<Steps.Root count={items.length}>
<Steps.List>
{items.map((item, index) => (
<Steps.Item key={index} index={index}>
<Steps.Trigger>
<Steps.Indicator>{index + 1}</Steps.Indicator>
<span>{item.title}</span>
</Steps.Trigger>
<Steps.Separator />
</Steps.Item>
))}
</Steps.List>
{items.map((item, index) => (
<Steps.Content key={index} index={index}>
{item.title} - {item.description}
</Steps.Content>
))}
<Steps.CompletedContent>Steps Complete - Thank you for filling out the form!</Steps.CompletedContent>
<div>
<Steps.PrevTrigger>Back</Steps.PrevTrigger>
<Steps.NextTrigger>Next</Steps.NextTrigger>
</div>
</Steps.Root>
)
}
import { Steps } from '@ark-ui/solid/steps'
import { For } from 'solid-js'
const items = [
{ value: 'first', title: 'First', description: 'Contact Info' },
{ value: 'second', title: 'Second', description: 'Date & Time' },
{ value: 'third', title: 'Third', description: 'Select Rooms' },
]
export const Basic = () => {
return (
<Steps.Root count={items.length}>
<Steps.List>
<For each={items}>
{(item, index) => (
<Steps.Item index={index()}>
<Steps.Trigger>
<Steps.Indicator>{index() + 1}</Steps.Indicator>
<span>{item.title}</span>
</Steps.Trigger>
<Steps.Separator />
</Steps.Item>
)}
</For>
</Steps.List>
<For each={items}>
{(item, index) => (
<Steps.Content index={index()}>
{item.title} - {item.description}
</Steps.Content>
)}
</For>
<Steps.CompletedContent>Steps Complete - Thank you for filling out the form!</Steps.CompletedContent>
<div>
<Steps.PrevTrigger>Back</Steps.PrevTrigger>
<Steps.NextTrigger>Next</Steps.NextTrigger>
</div>
</Steps.Root>
)
}
<script setup lang="ts">
import { Steps } from '@ark-ui/vue/steps'
const items = [
{ value: 'first', title: 'First', description: 'Contact Info' },
{ value: 'second', title: 'Second', description: 'Date & Time' },
{ value: 'third', title: 'Third', description: 'Select Rooms' },
]
</script>
<template>
<Steps.Root :count="items.length">
<Steps.List>
<Steps.Item v-for="(item, index) in items" :key="index" :index="index">
<Steps.Trigger>
<Steps.Indicator>{{ index + 1 }}</Steps.Indicator>
<span>{{ item.title }}</span>
</Steps.Trigger>
<Steps.Separator />
</Steps.Item>
</Steps.List>
<Steps.Content v-for="(item, index) in items" :key="index" :index="index">
{{ item.title }} - {{ item.description }}
</Steps.Content>
<Steps.CompletedContent>Steps Complete - Thank you for filling out the form!</Steps.CompletedContent>
<div>
<Steps.PrevTrigger>Back</Steps.PrevTrigger>
<Steps.NextTrigger>Next</Steps.NextTrigger>
</div>
</Steps.Root>
</template>
<script>
import { Steps } from '@ark-ui/svelte/steps'
const items = [
{ value: 'first', title: 'First', description: 'Contact Info' },
{ value: 'second', title: 'Second', description: 'Date & Time' },
{ value: 'third', title: 'Third', description: 'Select Rooms' },
]
</script>
<Steps.Root count={items.length}>
<Steps.List>
{#each items as item, index}
<Steps.Item {index}>
<Steps.Trigger>
<Steps.Indicator>{index + 1}</Steps.Indicator>
<span>{item.title}</span>
</Steps.Trigger>
<Steps.Separator />
</Steps.Item>
{/each}
</Steps.List>
{#each items as item, index}
<Steps.Content {index}>
{item.title} - {item.description}
</Steps.Content>
{/each}
<Steps.CompletedContent>Steps Complete - Thank you for filling out the form!</Steps.CompletedContent>
<div>
<Steps.PrevTrigger>Back</Steps.PrevTrigger>
<Steps.NextTrigger>Next</Steps.NextTrigger>
</div>
</Steps.Root>
Controlled Steps
Using the RootProvider component, you can control the active step by using the step prop and handling the
onStepChange event.
import { Steps } from '@ark-ui/react/steps'
import { useState } from 'react'
const items = [
{ value: 'first', title: 'First', description: 'Contact Info' },
{ value: 'second', title: 'Second', description: 'Date & Time' },
{ value: 'third', title: 'Third', description: 'Select Rooms' },
]
export const Controlled = () => {
const [step, setStep] = useState(0)
return (
<div>
<div>
<strong>Current Step:</strong> {step}
<button onClick={() => setStep(0)}>Reset to First</button>
<button onClick={() => setStep(items.length - 1)}>Skip to Last</button>
</div>
<Steps.Root count={items.length} step={step} onStepChange={(details) => setStep(details.step)}>
<Steps.List>
{items.map((item, index) => (
<Steps.Item key={index} index={index}>
<Steps.Trigger>
<Steps.Indicator>{index + 1}</Steps.Indicator>
<span>{item.title}</span>
</Steps.Trigger>
<Steps.Separator />
</Steps.Item>
))}
</Steps.List>
{items.map((item, index) => (
<Steps.Content key={index} index={index}>
{item.title} - {item.description}
</Steps.Content>
))}
<Steps.CompletedContent>Steps Complete - Thank you for filling out the form!</Steps.CompletedContent>
<div>
<Steps.PrevTrigger>Back</Steps.PrevTrigger>
<Steps.NextTrigger>Next</Steps.NextTrigger>
</div>
</Steps.Root>
</div>
)
}
import { Steps } from '@ark-ui/solid/steps'
import { For, createSignal } from 'solid-js'
const items = [
{ value: 'first', title: 'First', description: 'Contact Info' },
{ value: 'second', title: 'Second', description: 'Date & Time' },
{ value: 'third', title: 'Third', description: 'Select Rooms' },
]
export const Controlled = () => {
const [step, setStep] = createSignal(0)
return (
<div>
<div>
<strong>Current Step:</strong> {step()}
<button onClick={() => setStep(0)}>Reset to First</button>
<button onClick={() => setStep(items.length - 1)}>Skip to Last</button>
</div>
<Steps.Root count={items.length} step={step()} onStepChange={(details) => setStep(details.step)}>
<Steps.List>
<For each={items}>
{(item, index) => (
<Steps.Item index={index()}>
<Steps.Trigger>
<Steps.Indicator>{index() + 1}</Steps.Indicator>
<span>{item.title}</span>
</Steps.Trigger>
<Steps.Separator />
</Steps.Item>
)}
</For>
</Steps.List>
<For each={items}>
{(item, index) => (
<Steps.Content index={index()}>
{item.title} - {item.description}
</Steps.Content>
)}
</For>
<Steps.CompletedContent>Steps Complete - Thank you for filling out the form!</Steps.CompletedContent>
<div>
<Steps.PrevTrigger>Back</Steps.PrevTrigger>
<Steps.NextTrigger>Next</Steps.NextTrigger>
</div>
</Steps.Root>
</div>
)
}
<script setup lang="ts">
import { Steps } from '@ark-ui/vue/steps'
import { ref } from 'vue'
const items = [
{ value: 'first', title: 'First', description: 'Contact Info' },
{ value: 'second', title: 'Second', description: 'Date & Time' },
{ value: 'third', title: 'Third', description: 'Select Rooms' },
]
const currentStep = ref(0)
</script>
<template>
<div>
<div>
<strong>Current Step:</strong>
{{ currentStep }}
<button @click="currentStep = 0">Reset to First</button>
<button @click="currentStep = items.length - 1">Skip to Last</button>
</div>
<Steps.Root v-model:step="currentStep" :count="items.length">
<Steps.List>
<Steps.Item v-for="(item, index) in items" :key="index" :index="index">
<Steps.Trigger>
<Steps.Indicator>{{ index + 1 }}</Steps.Indicator>
<span>{{ item.title }}</span>
</Steps.Trigger>
<Steps.Separator />
</Steps.Item>
</Steps.List>
<Steps.Content v-for="(item, index) in items" :key="index" :index="index">
{{ item.title }} - {{ item.description }}
</Steps.Content>
<Steps.CompletedContent>Steps Complete - Thank you for filling out the form!</Steps.CompletedContent>
<div>
<Steps.PrevTrigger>Back</Steps.PrevTrigger>
<Steps.NextTrigger>Next</Steps.NextTrigger>
</div>
</Steps.Root>
</div>
</template>
<script>
import { Steps } from '@ark-ui/svelte/steps'
const items = [
{ value: 'first', title: 'First', description: 'Contact Info' },
{ value: 'second', title: 'Second', description: 'Date & Time' },
{ value: 'third', title: 'Third', description: 'Select Rooms' },
]
let step = $state(0)
</script>
<div>
<div>Current step: {step}</div>
<Steps.Root bind:step count={items.length}>
<Steps.List>
{#each items as item, index}
<Steps.Item {index}>
<Steps.Trigger>
<Steps.Indicator>{index + 1}</Steps.Indicator>
<span>{item.title}</span>
</Steps.Trigger>
<Steps.Separator />
</Steps.Item>
{/each}
</Steps.List>
<Steps.Progress />
{#each items as item, index}
<Steps.Content {index}>
<h3>{item.title}</h3>
<p>{item.description}</p>
</Steps.Content>
{/each}
<Steps.CompletedContent>
<h3>Complete!</h3>
<p>Thank you for filling out the form!</p>
</Steps.CompletedContent>
<div>
<Steps.PrevTrigger>Back</Steps.PrevTrigger>
<Steps.NextTrigger>Next</Steps.NextTrigger>
</div>
</Steps.Root>
</div>
Root Provider
An alternative way to control the steps is to use the RootProvider component and the useSteps store hook.
This way you can access the steps state and methods from outside the steps.
import { Steps, useSteps } from '@ark-ui/react/steps'
const items = [
{ value: 'first', title: 'First', description: 'Contact Info' },
{ value: 'second', title: 'Second', description: 'Date & Time' },
{ value: 'third', title: 'Third', description: 'Select Rooms' },
]
export const RootProvider = () => {
const steps = useSteps({ count: items.length })
return (
<>
<button onClick={() => steps.resetStep()}>Reset</button>
<Steps.RootProvider value={steps}>
<Steps.List>
{items.map((item, index) => (
<Steps.Item key={index} index={index}>
<Steps.Trigger>
<Steps.Indicator>{index + 1}</Steps.Indicator>
<span>{item.title}</span>
</Steps.Trigger>
<Steps.Separator />
</Steps.Item>
))}
</Steps.List>
{items.map((item, index) => (
<Steps.Content key={index} index={index}>
{item.title} - {item.description}
</Steps.Content>
))}
<Steps.CompletedContent>Steps Complete - Thank you for filling out the form!</Steps.CompletedContent>
<div>
<Steps.PrevTrigger>Back</Steps.PrevTrigger>
<Steps.NextTrigger>Next</Steps.NextTrigger>
</div>
</Steps.RootProvider>
</>
)
}
import { Steps, useSteps } from '@ark-ui/solid/steps'
import { For } from 'solid-js'
const items = [
{ value: 'first', title: 'First', description: 'Contact Info' },
{ value: 'second', title: 'Second', description: 'Date & Time' },
{ value: 'third', title: 'Third', description: 'Select Rooms' },
]
export const RootProvider = () => {
const steps = useSteps({ count: items.length })
return (
<>
<button onClick={() => steps().resetStep()}>Reset</button>
<Steps.RootProvider value={steps}>
<Steps.List>
<For each={items}>
{(item, index) => (
<Steps.Item index={index()}>
<Steps.Trigger>
<Steps.Indicator>{index() + 1}</Steps.Indicator>
<span>{item.title}</span>
</Steps.Trigger>
<Steps.Separator />
</Steps.Item>
)}
</For>
</Steps.List>
<For each={items}>
{(item, index) => (
<Steps.Content index={index()}>
{item.title} - {item.description}
</Steps.Content>
)}
</For>
<Steps.CompletedContent>Steps Complete - Thank you for filling out the form!</Steps.CompletedContent>
<div>
<Steps.PrevTrigger>Back</Steps.PrevTrigger>
<Steps.NextTrigger>Next</Steps.NextTrigger>
</div>
</Steps.RootProvider>
</>
)
}
<script setup lang="ts">
import { Steps, useSteps } from '@ark-ui/vue/steps'
const items = [
{ value: 'first', title: 'First', description: 'Contact Info' },
{ value: 'second', title: 'Second', description: 'Date & Time' },
{ value: 'third', title: 'Third', description: 'Select Rooms' },
]
const steps = useSteps({ count: items.length })
const resetStep = () => steps.value.resetStep()
</script>
<template>
<button @click="resetStep">Reset</button>
<Steps.RootProvider :value="steps">
<Steps.List>
<Steps.Item v-for="(item, index) in items" :key="index" :index="index">
<Steps.Trigger>
<Steps.Indicator>{{ index + 1 }}</Steps.Indicator>
<span>{{ item.title }}</span>
</Steps.Trigger>
<Steps.Separator />
</Steps.Item>
</Steps.List>
<Steps.Content v-for="(item, index) in items" :key="index" :index="index">
{{ item.title }} - {{ item.description }}
</Steps.Content>
<Steps.CompletedContent>Steps Complete - Thank you for filling out the form!</Steps.CompletedContent>
<div>
<Steps.PrevTrigger>Back</Steps.PrevTrigger>
<Steps.NextTrigger>Next</Steps.NextTrigger>
</div>
</Steps.RootProvider>
</template>
<script>
import { Steps, useSteps } from '@ark-ui/svelte/steps'
const items = [
{ value: 'first', title: 'First', description: 'Contact Info' },
{ value: 'second', title: 'Second', description: 'Date & Time' },
{ value: 'third', title: 'Third', description: 'Select Rooms' },
]
const id = $props.id()
const steps = useSteps({
id,
count: items.length,
defaultStep: 0,
})
</script>
<div>
<div>Current step: {steps().value}</div>
<Steps.RootProvider value={steps}>
<Steps.List>
{#each items as item, index}
<Steps.Item {index}>
<Steps.Trigger>
<Steps.Indicator>{index + 1}</Steps.Indicator>
<span>{item.title}</span>
</Steps.Trigger>
<Steps.Separator />
</Steps.Item>
{/each}
</Steps.List>
<Steps.Progress />
{#each items as item, index}
<Steps.Content {index}>
<h3>{item.title}</h3>
<p>{item.description}</p>
</Steps.Content>
{/each}
<Steps.CompletedContent>
<h3>Complete!</h3>
<p>Thank you for filling out the form!</p>
</Steps.CompletedContent>
<div>
<Steps.PrevTrigger>Back</Steps.PrevTrigger>
<Steps.NextTrigger>Next</Steps.NextTrigger>
</div>
</Steps.RootProvider>
</div>
If you're using the
RootProvidercomponent, you don't need to use theRootcomponent.
API Reference
Root
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
count | numberThe total number of steps | |
defaultStep | numberThe initial value of the stepper when rendered. Use when you don't need to control the value of the stepper. | |
ids | ElementIdsThe custom ids for the stepper elements | |
linear | booleanIf `true`, the stepper requires the user to complete the steps in order | |
onStepChange | (details: StepChangeDetails) => voidCallback to be called when the value changes | |
onStepComplete | VoidFunctionCallback to be called when a step is completed | |
orientation | 'horizontal' | 'horizontal' | 'vertical'The orientation of the stepper |
step | numberThe controlled value of the stepper |
| CSS Variable | Description |
|---|---|
--percent | The percent value for the Root |
| Data Attribute | Value |
|---|---|
[data-scope] | steps |
[data-part] | root |
[data-orientation] | The orientation of the steps |
CompletedContent
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
Content
| Prop | Default | Type |
|---|---|---|
index | number | |
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Data Attribute | Value |
|---|---|
[data-scope] | steps |
[data-part] | content |
[data-state] | "open" | "closed" |
[data-orientation] | The orientation of the content |
Indicator
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Data Attribute | Value |
|---|---|
[data-scope] | steps |
[data-part] | indicator |
[data-complete] | Present when the indicator value is complete |
[data-current] | Present when current |
[data-incomplete] |
Item
| Prop | Default | Type |
|---|---|---|
index | number | |
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Data Attribute | Value |
|---|---|
[data-scope] | steps |
[data-part] | item |
[data-orientation] | The orientation of the item |
List
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Data Attribute | Value |
|---|---|
[data-scope] | steps |
[data-part] | list |
[data-orientation] | The orientation of the list |
NextTrigger
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
PrevTrigger
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
Progress
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Data Attribute | Value |
|---|---|
[data-scope] | steps |
[data-part] | progress |
[data-complete] | Present when the progress value is complete |
RootProvider
| Prop | Default | Type |
|---|---|---|
value | UseStepsReturn | |
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
Separator
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Data Attribute | Value |
|---|---|
[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 |
[data-incomplete] |
Trigger
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Data Attribute | Value |
|---|---|
[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 |
[data-incomplete] |