Progress Steps displays the progress of a multi-step process, showing users where they are in a sequence of steps. It can be used both as a static indicator and as interactive navigation.
Installation
yarn add @twilio-paste/progress-steps
Usage
import {
ProgressSteps,
ProgressStepComplete,
ProgressStepCurrent,
ProgressStepIncomplete,
ProgressStepSeparator,
} from '@twilio-paste/core/progress-steps';
const MyComponent = () => {
return (
<ProgressSteps>
<ProgressStepComplete as="div">Account Info</ProgressStepComplete>
<ProgressStepSeparator />
<ProgressStepCurrent as="div">Verify Email</ProgressStepCurrent>
<ProgressStepSeparator />
<ProgressStepIncomplete as="div">Complete Profile</ProgressStepIncomplete>
</ProgressSteps>
);
};
Components
ProgressSteps
Container component for the progress steps.
orientation
'horizontal' | 'vertical'
default:"'horizontal'"
Layout direction of the progress steps.
Progress step components and separators.
element
string
default:"'PROGRESS_STEPS'"
Overrides the default element name for customization.
ProgressStepComplete
Represents a completed step.
as
'div' | 'button' | 'a'
required
HTML element to render:
div: Static, non-interactive step
button: Interactive step with click handler
a: Navigable step with href
Click handler (required when as="button").
URL (required when as="a").
ProgressStepCurrent
Represents the current active step.
as
'div' | 'button' | 'a'
required
HTML element to render.
ProgressStepIncomplete
Represents a step not yet reached.
as
'div' | 'button' | 'a'
required
HTML element to render.
Click handler (when as="button").
ProgressStepError
Represents a step with an error.
as
'div' | 'button' | 'a'
required
HTML element to render.
Click handler (when as="button").
ProgressStepSeparator
Visual separator between steps (renders as a line).
Examples
Horizontal Progress Steps
import {
ProgressSteps,
ProgressStepComplete,
ProgressStepCurrent,
ProgressStepIncomplete,
ProgressStepSeparator,
} from '@twilio-paste/core/progress-steps';
<ProgressSteps>
<ProgressStepComplete as="div">Step 1</ProgressStepComplete>
<ProgressStepSeparator />
<ProgressStepComplete as="div">Step 2</ProgressStepComplete>
<ProgressStepSeparator />
<ProgressStepCurrent as="div">Step 3</ProgressStepCurrent>
<ProgressStepSeparator />
<ProgressStepIncomplete as="div">Step 4</ProgressStepIncomplete>
</ProgressSteps>
Vertical Progress Steps
import {
ProgressSteps,
ProgressStepComplete,
ProgressStepCurrent,
ProgressStepIncomplete,
} from '@twilio-paste/core/progress-steps';
<ProgressSteps orientation="vertical">
<ProgressStepComplete as="div">Create Account</ProgressStepComplete>
<ProgressStepComplete as="div">Verify Email</ProgressStepComplete>
<ProgressStepCurrent as="div">Add Payment</ProgressStepCurrent>
<ProgressStepIncomplete as="div">Complete Setup</ProgressStepIncomplete>
</ProgressSteps>
Interactive Steps (Clickable)
import {
ProgressSteps,
ProgressStepComplete,
ProgressStepCurrent,
ProgressStepIncomplete,
ProgressStepSeparator,
} from '@twilio-paste/core/progress-steps';
import { useState } from 'react';
const InteractiveSteps = () => {
const [currentStep, setCurrentStep] = useState(2);
return (
<ProgressSteps>
<ProgressStepComplete
as="button"
onClick={() => setCurrentStep(0)}
>
Account Details
</ProgressStepComplete>
<ProgressStepSeparator />
{currentStep === 1 ? (
<ProgressStepCurrent as="button">
Billing Info
</ProgressStepCurrent>
) : (
<ProgressStepComplete
as="button"
onClick={() => setCurrentStep(1)}
>
Billing Info
</ProgressStepComplete>
)}
<ProgressStepSeparator />
{currentStep === 2 ? (
<ProgressStepCurrent as="div">
Review & Submit
</ProgressStepCurrent>
) : (
<ProgressStepIncomplete as="div">
Review & Submit
</ProgressStepIncomplete>
)}
</ProgressSteps>
);
};
With Links (Multi-page Flow)
import {
ProgressSteps,
ProgressStepComplete,
ProgressStepCurrent,
ProgressStepIncomplete,
ProgressStepSeparator,
} from '@twilio-paste/core/progress-steps';
<ProgressSteps>
<ProgressStepComplete as="a" href="/setup/account">
Account
</ProgressStepComplete>
<ProgressStepSeparator />
<ProgressStepCurrent as="a" href="/setup/team">
Team
</ProgressStepCurrent>
<ProgressStepSeparator />
<ProgressStepIncomplete as="div">
Preferences
</ProgressStepIncomplete>
</ProgressSteps>
With Error State
import {
ProgressSteps,
ProgressStepComplete,
ProgressStepError,
ProgressStepIncomplete,
ProgressStepSeparator,
} from '@twilio-paste/core/progress-steps';
<ProgressSteps>
<ProgressStepComplete as="div">Personal Info</ProgressStepComplete>
<ProgressStepSeparator />
<ProgressStepError as="button" onClick={() => console.log('Fix error')}>
Payment Method
</ProgressStepError>
<ProgressStepSeparator />
<ProgressStepIncomplete as="div">Confirmation</ProgressStepIncomplete>
</ProgressSteps>
With Descriptions
import {
ProgressSteps,
ProgressStepComplete,
ProgressStepCurrent,
ProgressStepIncomplete,
ProgressStepSeparator,
ProgressStepContent,
} from '@twilio-paste/core/progress-steps';
<ProgressSteps orientation="vertical">
<ProgressStepComplete as="div">
<ProgressStepContent>
Account Setup
<div>Your account has been created</div>
</ProgressStepContent>
</ProgressStepComplete>
<ProgressStepCurrent as="div">
<ProgressStepContent>
Email Verification
<div>Check your inbox for a verification link</div>
</ProgressStepContent>
</ProgressStepCurrent>
<ProgressStepIncomplete as="div">
<ProgressStepContent>
Complete Profile
<div>Add your preferences and settings</div>
</ProgressStepContent>
</ProgressStepIncomplete>
</ProgressSteps>
Onboarding Flow
import {
ProgressSteps,
ProgressStepComplete,
ProgressStepCurrent,
ProgressStepIncomplete,
} from '@twilio-paste/core/progress-steps';
import { Box } from '@twilio-paste/core/box';
import { Heading } from '@twilio-paste/core/heading';
const OnboardingFlow = ({ step }) => {
return (
<Box>
<Heading as="h2" variant="heading20">Get Started</Heading>
<ProgressSteps orientation="vertical">
<ProgressStepComplete as="div">
Create your account
</ProgressStepComplete>
{step >= 2 ? (
<ProgressStepComplete as="div">
Set up your workspace
</ProgressStepComplete>
) : step === 1 ? (
<ProgressStepCurrent as="div">
Set up your workspace
</ProgressStepCurrent>
) : (
<ProgressStepIncomplete as="div">
Set up your workspace
</ProgressStepIncomplete>
)}
{step >= 3 ? (
<ProgressStepComplete as="div">
Invite your team
</ProgressStepComplete>
) : step === 2 ? (
<ProgressStepCurrent as="div">
Invite your team
</ProgressStepCurrent>
) : (
<ProgressStepIncomplete as="div">
Invite your team
</ProgressStepIncomplete>
)}
</ProgressSteps>
</Box>
);
};
Accessibility
- Progress steps use semantic list markup with
role="list" and role="listitem"
- Each step state is visually distinct through icons and colors
- Interactive steps include proper focus states and keyboard support
- Links and buttons include appropriate ARIA attributes
- Step separators are decorative and hidden from screen readers
- Current step is emphasized for screen reader users
Best Practices
- Use progress steps for processes with 3-7 steps (fewer for mobile)
- Keep step labels concise and action-oriented
- Use horizontal orientation for shorter step lists, vertical for longer ones
- Make completed steps clickable only when users can return to previous steps
- Don’t allow users to skip ahead to incomplete steps
- Use ProgressStepError to highlight steps requiring attention
- Show the current step clearly with ProgressStepCurrent
- Consider using ProgressStepContent for additional context in vertical layouts
- For very long processes, consider breaking into multiple sub-processes
- Test with keyboard navigation to ensure all interactive steps are accessible
- On mobile, consider using vertical orientation or a simplified indicator