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
RootProvider
component, you don't need to use theRoot
component.
API Reference
Root
Prop | Default | Type |
---|---|---|
asChild | boolean Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
count | number The total number of steps | |
defaultStep | number The initial value of the stepper when rendered. Use when you don't need to control the value of the stepper. | |
ids | ElementIds The custom ids for the stepper elements | |
linear | boolean If `true`, the stepper requires the user to complete the steps in order | |
onStepChange | (details: StepChangeDetails) => void Callback to be called when the value changes | |
onStepComplete | VoidFunction Callback to be called when a step is completed | |
orientation | 'horizontal' | 'horizontal' | 'vertical' The orientation of the stepper |
step | number The 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 | boolean Use 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 | boolean Use 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 | boolean Use 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 | boolean Use 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 | boolean Use 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 | boolean Use 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 | boolean Use 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 | boolean Use 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 | boolean Use 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 | boolean Use 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 | boolean Use 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] |