useAnimate
Reactive Web Animations API wrapper. Drives element.animate() with Observable-based reactive state for playState, currentTime, playbackRate, and pending.
Basic Usage
Section titled “Basic Usage”import { function useRef$<T extends Element = Element>(externalRef?: React.Ref<T> | null): Ref$<T>
Creates an observable element ref. Can be used as a drop-in replacement for
useRef, composed with callback refs, or used with forwardRef.
The element is wrapped with opaqueObject to prevent legendapp/state
from making DOM properties reactive (deep observation).
useRef$ } from "@usels/core";import { import useAnimate
useAnimate } from "@usels/core";
function function MyComponent(): React.JSX.Element
MyComponent() { const const el$: Ref$<HTMLSpanElement>
el$ = useRef$<HTMLSpanElement>(externalRef?: React.Ref<HTMLSpanElement> | undefined): Ref$<HTMLSpanElement>
Creates an observable element ref. Can be used as a drop-in replacement for
useRef, composed with callback refs, or used with forwardRef.
The element is wrapped with opaqueObject to prevent legendapp/state
from making DOM properties reactive (deep observation).
useRef$<interface HTMLSpanElement
HTMLSpanElement>(); const { const isSupported$: any
isSupported$, const animate$: any
animate$, const play: any
play, const pause: any
pause, const reverse: any
reverse, const finish: any
finish, const cancel: any
cancel, const pending$: any
pending$, const playState$: any
playState$, const replaceState$: any
replaceState$, const startTime$: any
startTime$, const currentTime$: any
currentTime$, const timeline$: any
timeline$, const playbackRate$: any
playbackRate$, } = import useAnimate
useAnimate(const el$: Ref$<HTMLSpanElement>
el$, { transform: string
transform: "rotate(360deg)" }, 1000);
return ( <React.JSX.IntrinsicElements.span: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>
span React.RefAttributes<HTMLSpanElement>.ref?: React.Ref<HTMLSpanElement> | undefined
Allows getting a ref to the component instance.
Once the component unmounts, React will set ref.current to null
(or call the ref with null if you passed a callback ref).
ref={const el$: Ref$<HTMLSpanElement>
el$} React.HTMLAttributes<HTMLSpanElement>.style?: React.CSSProperties | undefined
style={{ StandardLonghandProperties<string | number, string & {}>.display?: Property.Display | undefined
This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015.
Syntax: [ <display-outside> || <display-inside> ] | <display-listitem> | <display-internal> | <display-box> | <display-legacy>
Initial value: inline
| Chrome | Firefox | Safari | Edge | IE |
| :----: | :-----: | :----: | :----: | :---: |
| 1 | 1 | 1 | 12 | 4 |
display: "inline-block" }}> useAnimate </React.JSX.IntrinsicElements.span: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>
span> );}Custom Keyframes
Section titled “Custom Keyframes”Array, object, or Observable keyframes are all supported.
import { function useRef$<T extends Element = Element>(externalRef?: React.Ref<T> | null): Ref$<T>
Creates an observable element ref. Can be used as a drop-in replacement for
useRef, composed with callback refs, or used with forwardRef.
The element is wrapped with opaqueObject to prevent legendapp/state
from making DOM properties reactive (deep observation).
useRef$ } from "@usels/core";import { import useAnimate
useAnimate } from "@usels/core";import { function observable<T>(): Observable<T | undefined> (+2 overloads)
observable } from "@legendapp/state";
function function MyComponent(): void
MyComponent() { const const el$: Ref$<HTMLDivElement>
el$ = useRef$<HTMLDivElement>(externalRef?: React.Ref<HTMLDivElement> | undefined): Ref$<HTMLDivElement>
Creates an observable element ref. Can be used as a drop-in replacement for
useRef, composed with callback refs, or used with forwardRef.
The element is wrapped with opaqueObject to prevent legendapp/state
from making DOM properties reactive (deep observation).
useRef$<interface HTMLDivElement
HTMLDivElement>();
// Object form (PropertyIndexedKeyframes) import useAnimate
useAnimate(const el$: Ref$<HTMLDivElement>
el$, { transform: string
transform: "rotate(360deg)" }, 1000);
// Array form import useAnimate
useAnimate(const el$: Ref$<HTMLDivElement>
el$, [{ transform: string
transform: "rotate(0deg)" }, { transform: string
transform: "rotate(360deg)" }], 1000);
// Observable — effect updates when value changes const const keyframes$: any
keyframes$ = observable<unknown>(value: Promise<unknown> | (() => unknown) | unknown): any (+2 overloads)
observable([ { clipPath: string
clipPath: "circle(20% at 0% 30%)" }, { clipPath: string
clipPath: "circle(20% at 50% 80%)" }, ]); import useAnimate
useAnimate(const el$: Ref$<HTMLDivElement>
el$, const keyframes$: any
keyframes$, 1000);}Options
Section titled “Options”Pass a number as duration shorthand, or an options object with full configuration.
useAnimate(el$, keyframes, { duration: 1000, immediate: true, // auto-play on mount (default: true) commitStyles: false, // commit styles on finish (default: false) persist: false, // persist animation (default: false) onReady(animate) { console.log("ready", animate); }, onError(e) { console.error(e); },});Delaying Start
Section titled “Delaying Start”import { function useRef$<T extends Element = Element>(externalRef?: React.Ref<T> | null): Ref$<T>
Creates an observable element ref. Can be used as a drop-in replacement for
useRef, composed with callback refs, or used with forwardRef.
The element is wrapped with opaqueObject to prevent legendapp/state
from making DOM properties reactive (deep observation).
useRef$ } from "@usels/core";import { import useAnimate
useAnimate } from "@usels/core";
function function MyComponent(): React.JSX.Element
MyComponent() { const const el$: Ref$<HTMLDivElement>
el$ = useRef$<HTMLDivElement>(externalRef?: React.Ref<HTMLDivElement> | undefined): Ref$<HTMLDivElement>
Creates an observable element ref. Can be used as a drop-in replacement for
useRef, composed with callback refs, or used with forwardRef.
The element is wrapped with opaqueObject to prevent legendapp/state
from making DOM properties reactive (deep observation).
useRef$<interface HTMLDivElement
HTMLDivElement>(); const { const play: any
play } = import useAnimate
useAnimate( const el$: Ref$<HTMLDivElement>
el$, { opacity: {}
opacity: [0, 1] }, { duration: number
duration: 1000, immediate: boolean
immediate: false, } );
return <React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button React.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefined
onClick={const play: any
play}>Start Animation</React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button>;}Type Declarations
Section titled “Type Declarations”export interface UseAnimateOptions extends KeyframeAnimationOptions, ConfigurableWindow { immediate?: boolean; commitStyles?: boolean; persist?: boolean; playbackRate?: number; onReady?: (animate: Animation) => void; onError?: (e: unknown) => void;}export type UseAnimateKeyframes = MaybeObservable<Keyframe[] | PropertyIndexedKeyframes | null>;export interface UseAnimateReturn { isSupported$: ReadonlyObservable<boolean>; animate$: ReadonlyObservable<Animation | null>; play: Fn; pause: Fn; reverse: Fn; finish: Fn; cancel: Fn; pending$: ReadonlyObservable<boolean>; playState$: ReadonlyObservable<AnimationPlayState>; replaceState$: ReadonlyObservable<AnimationReplaceState>; startTime$: Observable<number | null>; currentTime$: Observable<number | null>; timeline$: Observable<AnimationTimeline | null>; playbackRate$: Observable<number>;}export declare function useAnimate(target: MaybeElement, keyframes: UseAnimateKeyframes, options?: number | DeepMaybeObservable<UseAnimateOptions>): UseAnimateReturn;Source
Section titled “Source”Contributors
Section titled “Contributors”- tigerwest
Changelog
Section titled “Changelog”a7392ab2026-03-06 - feat(core,browser): add sync strategy hooks (tigerwest)