Stepper
A vertical, numbered list of steps with an optional active step that can follow the scroll position.
Stepper lays out a short sequence as a vertical, numbered list: a number in a
circle for each step, a 1px line joining it to the next, and the title and
description to the right. activeStep highlights one step in Base blue. It is
a compound component: the stepper provides the list, and Stepper.Step,
Stepper.Title and Stepper.Description place the content. Steps number
themselves in the order they appear. The slots are also named exports
(StepperStep, StepperTitle, StepperDescription); use those in a React
Server Component, where the Stepper.Step properties of the client module are
not available.
import { Stepper } from '@base-org/bds-web/product/stepper';
<Stepper activeStep={0}>
<Stepper.Step>
<Stepper.Title>Deposit</Stepper.Title>
<Stepper.Description>A customer deposits supported stablecoins in your app.</Stepper.Description>
</Stepper.Step>
<Stepper.Step>
<Stepper.Title>Earn</Stepper.Title>
<Stepper.Description>Deposits earn a variable rate from borrowers across lending markets on Base.</Stepper.Description>
</Stepper.Step>
</Stepper>;Variants
Steppers are vertical only for now.
Medium
The default, size="md", on the Product scale: a 24px number in
label-medium, titles in title-3, descriptions in body, and 24px between
steps.
Deposit
A customer deposits supported stablecoins in your app.
Earn
Deposits earn a variable rate from borrowers across lending markets on Base.
Withdraw
Customers can withdraw and stop earning, subject to available liquidity and protocol terms.
<Stepper activeStep={0}>
<Stepper.Step>
<Stepper.Title>Deposit</Stepper.Title>
<Stepper.Description>A customer deposits supported stablecoins in your app.</Stepper.Description>
</Stepper.Step>
<Stepper.Step>
<Stepper.Title>Earn</Stepper.Title>
<Stepper.Description>Deposits earn a variable rate from borrowers across lending markets on Base.</Stepper.Description>
</Stepper.Step>
<Stepper.Step>
<Stepper.Title>Withdraw</Stepper.Title>
<Stepper.Description>Customers can withdraw and stop earning, subject to available liquidity and protocol terms.</Stepper.Description>
</Stepper.Step>
</Stepper>Large
size="lg" is the larger marketing scale: a 32px number, titles on the brand
title-3 step, descriptions on the brand body step up to 520px wide, and
40px between steps. Use it for a sequence in a marketing section, such as the
How it works list on base.org.
Deposit
A customer deposits supported stablecoins in your app.
Earn
Deposits earn a variable rate from borrowers across lending markets on Base.
Withdraw
Customers can withdraw and stop earning, subject to available liquidity and protocol terms.
<Stepper size="lg" activeStep={1}>
<Stepper.Step>
<Stepper.Title>Deposit</Stepper.Title>
<Stepper.Description>A customer deposits supported stablecoins in your app.</Stepper.Description>
</Stepper.Step>
<Stepper.Step>
<Stepper.Title>Earn</Stepper.Title>
<Stepper.Description>Deposits earn a variable rate from borrowers across lending markets on Base.</Stepper.Description>
</Stepper.Step>
<Stepper.Step>
<Stepper.Title>Withdraw</Stepper.Title>
<Stepper.Description>Customers can withdraw and stop earning, subject to available liquidity and protocol terms.</Stepper.Description>
</Stepper.Step>
</Stepper>| Size | Use for |
|---|---|
md | The default: a sequence on a product surface, such as setup steps. |
lg | A sequence in a marketing section, with a 32px number and brand type steps. |
No active step
Leave out activeStep and every step stays gray: a plain numbered list with
nothing highlighted.
Create a key
Add it to your app
<Stepper>
<Stepper.Step>
<Stepper.Title>Create a key</Stepper.Title>
</Stepper.Step>
<Stepper.Step>
<Stepper.Title>Add it to your app</Stepper.Title>
</Stepper.Step>
</Stepper>Scroll tracking
useStepperScroll(ref, count) returns the index of the step whose vertical
span the centre of the viewport is passing through. Give it the stepper's
ref and the number of steps, and pass the result to activeStep. The
highlight moves forwards and backwards as the reader scrolls, and the range
scales with the list's own height rather than the viewport's. Scroll the page
past this example to see it.
Choose an amount
A customer picks how much to borrow against the crypto they already hold in your app.
Get the loan instantly
They receive USDC straight away. Their collateral moves onchain and backs the loan.
Repay anytime
No schedule and no deadlines. Your customers repay when they want and get their collateral back.
'use client';
import { useRef } from 'react';
import { Stepper, useStepperScroll } from '@base-org/bds-web/product/stepper';
function HowItWorksSteps({ steps }) {
const ref = useRef<HTMLOListElement>(null);
const activeStep = useStepperScroll(ref, steps.length);
return (
<Stepper ref={ref} size="lg" activeStep={activeStep}>
{steps.map((step) => (
<Stepper.Step key={step.title}>
<Stepper.Title>{step.title}</Stepper.Title>
<Stepper.Description>{step.description}</Stepper.Description>
</Stepper.Step>
))}
</Stepper>
);
}The number's colour fades over 500ms as the active step changes, a slow change that follows scrolling rather than a direct response to input. Under reduced motion it changes at once.
When to use
Use Stepper to show a short, ordered sequence a reader follows in order, such as the steps of a flow or a How it works list.
Use something else when:
- The items have no order. Use a Card Group of Cards, or a plain list.
- Each step is a tile in a grid. Use a numbered Card
with
Card.Number. - The reader moves between views of the same content. Use Tabs or Segment.
The base.org How it works section, with its heading, tabs and media, stays a copyable pattern built on Stepper rather than a package component.
Usage
Do
Keep a stepper to a handful of steps, each with a short title and one sentence of description.
Don't
Use the active step to show progress through a form the reader controls; Stepper does not make steps interactive or track completion.
- Put each step's content in
Stepper.Titleand an optionalStepper.Description. The number and line come from the step. - Make each
Stepper.Stepa direct child of the stepper, so it is numbered. activeStepis 0-based. Leave it out for no highlight.- Call
useStepperScrollin a client component, with the samerefthe stepper gets.
Props
Stepper
| Prop | Type | Default |
|---|---|---|
activeStepThe 0-based index of the step to highlight. Leave it out for no | Typenumber | Default— |
size`md` is the Product scale: a 24px number and `title-3` titles. `lg` is | Type"md" | "lg" | Defaultmd |
Stepper.Step takes the props of an li, Stepper.Title those of an h3,
and Stepper.Description those of a p; none adds props of its own. Each slot
reads the stepper's size, and the step's index and active state.
useStepperScroll
| Argument | Type | Description |
|---|---|---|
ref | RefObject<HTMLElement | null> | The ref passed to the stepper. |
count | number | The number of steps. |
It returns the 0-based index of the active step, starting at 0.
Accessibility
Stepper renders an ordered list and each step a list item, so assistive technology announces how many steps there are and where each one sits. The number in its circle and the connecting line are decorative and hidden; the list carries the order.
The active step has aria-current="step". With no activeStep, no step is
marked current.
Stepper.Title renders an h3, so the steps sit under the section's h2
heading.
There is no React Aria primitive for a stepper. Stepper is not interactive: it has no focus stop and no keyboard behaviour of its own, and links or buttons inside a step keep theirs.