Step Player React component for Tailwind CSS v4
v1.0.1Updated in 1.2.1
A React stories progress bar: an iOS-style stepped track with a play, pause and replay button, whose active step fills as it plays.
Press play to start the sequence. The current step stretches into a bar and fills as it runs, then shrinks to a filled dot when it hands off to the next one. Press again to pause mid-fill; the play icon splits into the pause bars as it goes. At the end it fades into a replay arrow. With seekable on, tap any step to jump to it and keep playing; the dots keep a finger-sized tap target and a visible focus ring for keyboard users.
Step Player is a free React component in the React media components category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add step-player, together with UIProvider and the npm packages flubber and motion.
Add to a project
Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup
npx wingo-ui@latest add step-playerUsage
Import StepPlayer from @/components/ui/step-player.
"use client"
import { useState } from "react"
import { StepPlayer } from "@/components/ui/step-player"
export function Demo() {
return <StepPlayer steps={5} duration={3000} />
}
// drive your own carousel off the index
const slides = ["Welcome", "Invite your team", "Ship it"]
export function Carousel() {
const [index, setIndex] = useState(0)
return (
<div className="grid gap-4">
<p className="text-lg font-medium">{slides[index]}</p>
<StepPlayer steps={slides.length} value={index} onValueChange={setIndex} loop />
</div>
)
}Props
stepsdefault4number | StepPlayerStep[]How many steps to show. Pass an array of { duration?, label? } to give each one its own duration or accessible label.
sizedefault48numberHeight of the track and button in pixels (minimum 12). Every other dimension is a fixed ratio of it, so the dots, bar, gaps, padding, icon, and tap targets all scale from this one number.
controlPositiondefault"right""left" | "right"Which side of the track the button sits on.
showControldefaulttruebooleanShows the round play button. Turn it off for a track on its own and drive it with the playing prop.
trackColordefault""stringAny CSS color for the pill track and the round control. Empty uses #F4F4F9 light, #262626 dark.
fillColordefault""stringAny CSS color for finished steps and the running fill. Empty uses #3C3C43 light, #EBEBF5 dark.
pendingColordefault""stringAny CSS color for the steps still to come.
iconColordefault""stringAny CSS color for the play, pause and replay glyph and the focus outline. Empty uses #868593.
durationdefault4000numberMilliseconds each step runs for. A per-step duration overrides it; 0 stops the timer so you can drive the index yourself.
loopdefaultfalsebooleanWraps back to the first step instead of stopping. The replay button never appears when this is on.
seekabledefaultfalsebooleanLets a step be clicked to jump to it (and starts playing from there). The dots keep a finger-sized tap target at every size.
playLabeldefault"Play"stringAccessible name of the control while paused.
pauseLabeldefault"Pause"stringAccessible name of the control while playing.
replayLabeldefault"Replay"stringAccessible name of the control after the last step.
positionLabeldefault"Step {index} of {count}"stringAccessible name of the track; {index} and {count} fill in.
stepLabeldefault"Step {index}"stringAccessible name of each seek target when seekable and a step has no label; {index} fills in.
defaultValuedefault0numberStep to start on when uncontrolled. Clamped to the last step.
defaultPlayingdefaultfalsebooleanWhether the sequence starts running on mount when uncontrolled.
valuenumberActive step index, for controlled use. Leave it out and the component tracks its own index.
onValueChange(value: number) => voidFires when the step advances, wraps, or is clicked. Also fires on replay.
playingbooleanPlay state, for controlled use. Leave it out to let the button drive it.
onPlayingChange(playing: boolean) => voidFires when the button is pressed, when a step is clicked, and when the last step ends without loop.
onComplete() => voidFires when the last step finishes, on every pass when looping.
classNamestringExtra classes merged onto the root element (data-slot="step-player"). It sizes itself from the size prop, so give it a width only if you want the track wider than its content. Other div props are spread onto it too.
Parts
StepPlayerStep
typeOne entry of steps when it is an array: { duration?, label? }. duration (ms) overrides the player's for that step, label names its seek target.
Examples
Onboarding stories
Controlled with value and onValueChange, so the slide follows the player; seekable lets a tap jump to any step.
Steps with their own length
An array of steps sets each duration and names each seek target; loop starts over at the end.
Over a photo
Light track, fill and icon colors with the control on the left, like a story viewer.
Files
- components/ui/step-player.tsx
FAQ
Is the React Step Player component free?
Yes. The source is free and needs no account: copy it from the Code tab or install it with the CLI.
How do I install Step Player?
Run npx wingo-ui@latest add step-player. It also adds the Wingo UI item it builds on (UIProvider) and the npm packages flubber and motion. With shadcn, run npx shadcn@latest add https://wingo-ui.com/r/step-player.json.