Skip to content

Steps

Steps uses a compound component pattern. The Step sub-component is re-exported from the same module.

import { Steps, Step } from '@delightstack/components';
import type { StepsContext } from '@delightstack/components';
Account Create your account
2
Profile Set up your profile
3
Review Review details
4
Complete All done
View code
<script>
import { Steps, Step } from '@delightstack/components';
import { Button } from '@delightstack/components';
let current = $state(1);
</script>
<Steps bind:current>
<Step title="Account" description="Create your account" />
<Step title="Profile" description="Set up your profile" />
<Step title="Review" description="Review details" />
<Step title="Complete" description="All done" />
</Steps>
<Button outline onclick={() => current = Math.max(0, current - 1)}>Back</Button>
<Button onclick={() => current = Math.min(4, current + 1)}>Next</Button>

Allow users to navigate back to completed steps by clicking on them.

Account
2
Profile
3
Review
4
Complete
Current step: 1 — Click any completed step to go back.
View code
<script>
import { Steps, Step } from '@delightstack/components';
let current = $state(1);
</script>
<Steps bind:current clickable>
<Step title="Account" />
<Step title="Profile" />
<Step title="Review" />
<Step title="Complete" />
</Steps>

Allow jumping to any step, not just completed ones.

1
Overview
Details
Summary
Current step: 0 — Click any step to jump directly to it.
View code
<script>
import { Steps, Step } from '@delightstack/components';
let current = $state(0);
</script>
<Steps bind:current clickable linear={false}>
<Step title="Overview" />
<Step title="Details" />
<Step title="Summary" />
</Steps>

Stack steps vertically for sidebar or mobile layouts.

Cart Review items
2
Shipping Enter address
3
Payment Add payment method
4
Confirm Place order
View code
<script>
import { Steps, Step } from '@delightstack/components';
import { Button } from '@delightstack/components';
let current = $state(1);
</script>
<Steps bind:current orientation="vertical">
<Step title="Cart" description="Review items" />
<Step title="Shipping" description="Enter address" />
<Step title="Payment" description="Add payment method" />
<Step title="Confirm" description="Place order" />
</Steps>
<Button outline onclick={() => current = Math.max(0, current - 1)}>Back</Button>
<Button onclick={() => current = Math.min(4, current + 1)}>Next</Button>

Mark steps with error states or as optional.

Account
Verification
3
Preferences Optional
4
Done
View code
<script>
import { Steps, Step } from '@delightstack/components';
let current = $state(2);
</script>
<Steps bind:current>
<Step title="Account" />
<Step title="Verification" error />
<Step title="Preferences" optional />
<Step title="Done" />
</Steps>

Render content for each step, shown when that step is active.

1
Personal Info
2
Address
3
Confirm

All done! Review your information and submit.

View code
<script>
import { Steps, Step } from '@delightstack/components';
import { Button, Input } from '@delightstack/components';
let current = $state(0);
</script>
<Steps bind:current orientation="vertical">
<Step title="Personal Info">
<Input label="Full Name" />
<Input label="Email" type="email" />
<Button onclick={() => current++}>Next</Button>
</Step>
<Step title="Address">
<Input label="Street" />
<Input label="City" />
<Button outline onclick={() => current--}>Back</Button>
<Button onclick={() => current++}>Next</Button>
</Step>
<Step title="Confirm">
<p>All done!</p>
<Button outline onclick={() => current--}>Back</Button>
</Step>
</Steps>

When horizontal steps can’t fit their container, the row becomes a snap-scrollable strip with edge fades hinting at the clipped steps. On devices with a fine pointer, chevron buttons appear at the edges to page through (no clunky horizontal scrollbar); on touch devices the strip swipes. The current step automatically scrolls into view when it changes, and nothing changes when the steps fit.

Account Sign up
Profile About you
3
Team Invite members
4
Billing Payment details
5
Plan Pick a tier
6
Integrations Connect tools
7
Review Check settings
8
Launch Go live
View code
<script>
import { Steps, Step } from '@delightstack/components';
let current = $state(2);
</script>
<!-- Constrained container: the steps overflow into a scroll strip -->
<div style="max-width: 28rem;">
<Steps bind:current clickable>
<Step title="Account" description="Sign up" />
<Step title="Profile" description="About you" />
<Step title="Team" description="Invite members" />
<Step title="Billing" description="Payment details" />
<Step title="Plan" description="Pick a tier" />
<Step title="Integrations" description="Connect tools" />
<Step title="Review" description="Check settings" />
<Step title="Launch" description="Go live" />
</Steps>
</div>
View code
<Steps skeleton={loading} skeleton_count={4}></Steps>
PropTypeDefaultDescription
currentnumber0The current active step index ($bindable)
orientation'horizontal' | 'vertical''horizontal'Layout orientation
clickablebooleanfalseWhether completed steps are clickable for navigation
linearbooleantrueWhether navigation is restricted to sequential order
size'0' | '1' | '2' | '3''1'Size variant
skeletonbooleanfalseShow skeleton shimmer placeholders
skeleton_countnumber4Number of skeleton placeholder steps
idstringautoElement ID
classstring''Additional CSS classes
childrenSnippetundefinedStep children
PropTypeDefaultDescription
titlestring''The title text for this step
descriptionstring''Optional description text below the title
optionalbooleanfalseWhether this step is optional
errorbooleanfalseWhether this step is in an error state
childrenSnippetundefinedStep content (for wizard mode, shown when active)
EventDetailDescription
onchange{ step: number }Fires when the active step changes
oncompletenoneFires when all steps are completed
interface StepsContext {
current: number;
orientation: 'horizontal' | 'vertical';
clickable: boolean;
linear: boolean;
size: string;
totalSteps: number;
register: () => number;
navigate: (index: number) => void;
}
  • Container uses role="navigation" with aria-label="Progress steps"
  • Each step uses role="listitem" with aria-current="step" for the active step
  • Step indicators show completion state with aria-label (e.g. “Step 1: Account - Completed”)
  • Clickable steps are focusable <button> elements
  • Error steps are announced via aria-label with error state
  • Optional steps display “(Optional)” text
  • Focus-visible outlines on clickable step indicators
  • Connector lines between steps indicate progress visually