An iOS-style stepped progress track with a play, pause and replay control. The active step stretches into a bar that 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. Steps can be clicked to jump.
Options you can pass to customize this component.
stepsnumberStepPlayerStep[]How many steps to show. Pass an array to give each one its own duration or label.
valuenumberActive step index, for controlled use. Leave it out and the component tracks its own index.
defaultValuenumberStep to start on when uncontrolled. Clamped to the last step.
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.
defaultPlayingbooleanWhether the sequence starts running on mount when uncontrolled.
onPlayingChange(playing: boolean) => voidFires when the button is pressed, and when the last step ends without loop.
durationnumberMilliseconds each step runs for. A per-step duration overrides it; 0 stops the timer so you can drive the index yourself.
loopbooleanWraps back to the first step instead of stopping. The replay button never appears when this is on.
onComplete() => voidFires when the last step finishes, on every pass when looping.
sizenumberHeight of the track and button in pixels. 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.
showControlbooleanShows the round play button. Turn it off for a track on its own and drive it with the playing prop.
controlPositionleftrightWhich side of the track the button sits on.
seekablebooleanLets a step be clicked to jump to it. The dots keep a finger-sized tap target at every size.
classNamestringExtra classes merged onto the root element. It sizes itself from the size prop, so give it a width only if you want the track wider than its content.
npx shadcn@latest add swamimalode07/rare-ui/step-playerimport { StepPlayer } from "@/components/ui/step-player"export function Demo() {return <StepPlayer steps={5} duration={3000} />}// drive your own carousel off the index<StepPlayersteps={slides.length}value={index}onValueChange={setIndex}loop/>
Click the code icon in the top-right corner to view the source code.
Most components here are recreations of great work from around the web. I don't claim to be the original creator - this is my attempt to reverse-engineer, replicate, and often add a few extra features. I've tried to credit everyone; if I missed someone, let me know.
1
Step 1 of 5