<script setup lang="ts">
import { StepperDescription, StepperIndicator, StepperItem, StepperRoot, StepperSeparator, StepperTitle, StepperTrigger } from 'akar';
import { akarDemoIcon } from '../akar-demo-icons';
import './styles.css';
const steps = [{
step: 1,
title: 'Address',
description: 'Add your address here',
icon: 'radix-icons:home',
}, {
step: 2,
title: 'Shipping',
description: 'Set your preferred shipping method',
icon: 'radix-icons:archive',
}, {
step: 3,
title: 'Checkout',
description: 'Confirm your order',
icon: 'radix-icons:check',
}];
</script>
<template>
<div class="akar-demo akar-demo-Stepper">
<StepperRoot
:default-value="2"
class="StepperList"
>
<StepperItem
v-for="item in steps"
:key="item.step"
class="StepperItem"
:step="item.step"
>
<StepperTrigger class="StepperTrigger">
<StepperIndicator
class="StepperIndicator"
>
<PIcon
:name="akarDemoIcon(item.icon)"
class="StepperIndicatorIcon"
/>
</StepperIndicator>
<div class="StepperItemText">
<StepperTitle class="StepperTitle">
{{ item.title }}
</StepperTitle>
<StepperDescription class="StepperDescription">
{{ item.description }}
</StepperDescription>
</div>
</StepperTrigger>
<StepperSeparator
v-if="item.step !== steps[steps.length - 1].step"
class="StepperSeparator"
/>
</StepperItem>
</StepperRoot>
</div>
</template>@import '../_colors/grass.css';
@import '../_colors/mauve.css';
@import '../_colors/green.css';
@scope (.akar-demo-Stepper) {
.StepperList {
display: flex;
gap: 1rem;
}
.StepperItem {
display: flex;
gap: 1rem;
align-items: center;
cursor: pointer;
}
.StepperItem[data-disabled] {
pointer-events: none;
}
.StepperTrigger {
display: flex;
flex-direction: column;
gap: 0.5rem;
align-items: center;
}
.StepperIndicator {
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 9999px;
width: 1.5rem;
height: 1.5rem;
color: var(--grass11);
box-shadow: 0 0 0 2px #000;
}
.StepperItem[data-disabled] .StepperIndicator {
color: #9ca3af;
}
.StepperItem[data-state='active'] .StepperIndicator {
background-color: #000;
color: #fff;
box-shadow: 0 0 0 2px #000;
}
.StepperItem[data-state='completed'] .StepperIndicator {
background-color: var(--green9);
color: #fff;
box-shadow: 0 0 0 2px var(--green9);
}
.StepperIcon {
width: 1.5rem;
height: 1.5rem;
}
.StepperTitle {
font-size: 0.875rem;
font-weight: 500;
color: #000;
}
.StepperDescription {
font-size: 0.75rem;
color: #000;
}
.StepperSeparator {
width: 20px;
height: 1px;
flex-shrink: 0;
background-color: var(--green5);
}
.StepperItem[data-state='active'] .StepperSeparator {
background-color: #000;
}
.StepperItem[data-state='completed'] .StepperSeparator {
background-color: var(--green9);
}
.StepperItem[data-disabled] {
background-color: #d1d5db;
}
}Features
- Can be controlled or uncontrolled.
- Supports horizontal/vertical orientation.
- Supports linear/non-linear activation.
- Full keyboard navigation.
Installation
Install the component from your command line.
pnpm add akar
yarn add akar
npm install akar
bun add akar
Anatomy
Import all parts and piece them together.
<script setup>
import { StepperDescription, StepperIndicator, StepperItem, StepperRoot, StepperTitle, StepperTrigger } from 'akar';
</script>
<template>
<StepperRoot>
<StepperItem>
<StepperTrigger />
<StepperIndicator />
<StepperTitle />
<StepperDescription />
<StepperSeparator />
</StepperItem>
</StepperRoot>
</template>
API Reference
Root
Contains all the stepper component parts.
| Prop | Default | Type |
|---|---|---|
as | "div" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
defaultValue | 1 | numberThe value of the step that should be active when initially rendered. Use when you do not need to control the state of the steps. |
dir | "ltr" | "rtl"The reading direction of the combobox when applicable. If omitted, inherits globally from | |
linear | true | booleanWhether or not the steps must be completed in order. |
modelValue | numberThe controlled value of the step to activate. Can be bound as | |
orientation | "horizontal" | "vertical" | "horizontal"The orientation the steps are laid out. Mainly so arrow navigation is done accordingly (left & right vs. up & down). |
| Event | Payload |
|---|---|
update:modelValue | payload: numberEvent handler called when the value changes |
| Slot (default) | Payload |
|---|---|
modelValue | numberCurrent step |
totalSteps | numberTotal number of steps |
isNextDisabled | booleanWhether or not the next step is disabled |
isPrevDisabled | booleanWhether or not the previous step is disabled |
isFirstStep | booleanWhether or not the first step is active |
isLastStep | booleanWhether or not the last step is active |
goToStep | (step: number): voidGo to a specific step |
nextStep | (): voidGo to the next step |
prevStep | (): voidGo to the previous step |
hasNext | (): booleanWhether or not there is a next step |
hasPrev | (): booleanWhether or not there is a previous step |
| Method | Type |
|---|---|
goToStep | (step: number) => void |
nextStep | () => void |
prevStep | () => void |
hasNext | () => boolean |
hasPrev | () => boolean |
| Data attribute | Value |
|---|---|
[data-orientation] | "vertical" | "horizontal" |
[data-linear] | Present when linear |
Item
The step item component.
| Prop | Default | Type |
|---|---|---|
as | "div" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
completed | false | booleanShows whether the step is completed. |
disabled | false | booleanWhen |
step* | numberA unique value that associates the stepper item with an index |
| Slot (default) | Payload |
|---|---|
state | "active" | "completed" | "inactive"The current state of the stepper item |
| Data attribute | Value |
|---|---|
[data-state] | "active" | "inactive" | "completed" |
[data-disabled] | Present when disabled |
[data-orientation] | "vertical" | "horizontal" |
Trigger
The trigger that toggles the step.
| Prop | Default | Type |
|---|---|---|
as | "button" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
| Data attribute | Value |
|---|---|
[data-state] | "active" | "inactive" | "completed" |
[data-disabled] | Present when disabled |
[data-orientation] | "vertical" | "horizontal" |
Indicator
The indicator for the step.
| Prop | Default | Type |
|---|---|---|
as | "span" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
| Slot (default) | Payload |
|---|---|
step | numberCurrent step |
Title
An accessible title to be announced when the stepper trigger is focused.
If you want to hide the title, wrap it inside our Visually Hidden utility like this <VisuallyHidden asChild>.
| Prop | Default | Type |
|---|---|---|
as | "h4" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
Description
An optional accessible description to be announced when the stepper trigger is focused.
If you want to hide the description, wrap it inside our Visually Hidden utility like this <VisuallyHidden asChild>. If you want to remove the description entirely, remove this part and pass aria-describedby="undefined" to StepperTrigger.
| Prop | Default | Type |
|---|---|---|
as | "div" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
completed | false | booleanShows whether the step is completed. |
disabled | false | booleanWhen |
step* | numberA unique value that associates the stepper item with an index |
| Slot (default) | Payload |
|---|---|
state | "active" | "completed" | "inactive"The current state of the stepper item |
Examples
Vertical
You can create vertical steps by using the orientation prop.
<script setup>
import { StepperDescription, StepperIndicator, StepperItem, StepperRoot, StepperTitle } from 'akar'
</script>
<template>
<StepperRoot
:default-value="1"
orientation="vertical"
>
<StepperItem>
<StepperIndicator />
<StepperTitle />
<StepperDescription />
</StepperItem>
<StepperItem>
<StepperIndicator />
<StepperTitle />
<StepperDescription />
</StepperItem>
</StepperRoot>
</template>
With controls
You can add additional controls for the stepper using buttons and access the typed component instance using useTemplateRef.
<script setup lang="ts">
const stepper = useTemplateRef('stepper')
</script>
<template>
<StepperRoot
ref="stepper"
:default-value="1"
>
<StepperItem>
<StepperIndicator />
<StepperTitle />
<StepperDescription />
</StepperItem>
<StepperItem>
<StepperIndicator />
<StepperTitle />
<StepperDescription />
</StepperItem>
</StepperRoot>
<div class="flex gap-2 justify-between mt-4">
<button
:disabled="!stepper?.hasPrev()"
@click="stepper?.prevStep()"
>
Prev
</button>
<button
:disabled="!stepper?.hasNext()"
@click="stepper?.nextStep()"
>
Next
</button>
</div>
</template>
Accessibility
Keyboard Interactions
| Key | Description |
|---|---|
| Tab | When focus moves onto the steps, focuses the first step . |
| ArrowDown | Moves focus to the next step depending on orientation. |
| ArrowRight | Moves focus to the next step depending on orientation . |
| ArrowUp | Moves focus to the previous step depending on orientation . |
| ArrowLeft | Moves focus to the previous step depending on orientation . |
| Enter/Space | Selects the focused step. |