Seeking and segments
Move the playhead, play part of the file, and wire up a scrub gesture.
seek moves the playhead without touching playback: playing stays playing, paused stays paused.
import { Lottie, type LottieHandle } from "lottie-react";import { useRef } from "react";export function SeekUnits() { const handle = useRef<LottieHandle>(null); return ( <> <Lottie src="/anim.json" autoplay loop lottieRef={handle} /> <div> <button type="button" onClick={() => handle.current?.seek(135)}> frame 135 </button> <button type="button" onClick={() => handle.current?.seek({ percent: 50 })} > 50% </button> <button type="button" onClick={() => handle.current?.seek({ seconds: 1 })} > 1s </button> <button type="button" onClick={() => handle.current?.seek({ marker: "middle" })} > marker </button> </div> </> );}The units
A plain number is a frame. Every other unit names itself:
lottie.seek(90);
lottie.seek({ frame: 90 });
lottie.seek({ percent: 50 });
lottie.seek({ seconds: 1.5 });
lottie.seek({ marker: "intro" });One unit per call: combining two is a compile error.
percent and seconds measure the currently playable range, and out-of-range frames clamp to it.
A marker resolves to the frame the designer placed it on; a marker outside the range clamps and warns, because that is an authoring mistake worth naming.
Play part of the file
playSegments narrows playback to a range and plays it:
playable frames: 0
import { LottieDisplay, useLottie } from "lottie-react";export function PlaySegments() { const lottie = useLottie({ src: "/anim.json", autoplay: true, loop: true }); return ( <> <LottieDisplay lottie={lottie} /> <p>playable frames: {lottie.playableFrames}</p> <div> <button type="button" onClick={() => lottie.playSegments([0, 90])}> first half </button> <button type="button" onClick={() => lottie.playSegments([90, 180])}> second half </button> <button type="button" onClick={() => lottie.playSegments({ marker: "middle" })} > marker span </button> <button type="button" onClick={() => lottie.resetSegments()}> whole file </button> </div> </> );}A range is [first, last], or { marker: "intro" } to use a marker's own duration; a marker without a duration is refused with a warning, because it is a point rather than a span.
playableFrames and playableDuration follow the active range, as the readout above shows.
resetSegments() returns playback to the whole file.
Pass { queue: true } as the second argument and the new range waits for the current pass to finish.
With nothing playing there is nothing to wait for, and the range applies at once.
Scrubbing
A drag gesture is three calls: scrubStart() once, scrubTo(frame) per move, scrubEnd() once.
scrubEnd resumes playback if it was playing when the drag began, and it is the only call that clears that memory, so an unfinished drag is a missing call rather than a silent surprise.