dioxus-animate 0.3.3

Time-based CSS class manipulation for smooth animations in Dioxus apps
Documentation

We're dsplce.co, check out our work on our website: dsplce.co πŸ–€

dioxus-animate

Dioxus crates.io Downloads crates.io Size License crates.io

✨ Time-based CSS class animations for Dioxus β€” think CSS keyframes, but driven by your app's logic.

dioxus-animate gives you one ergonomic macro to sequence CSS class additions and removals on a timeline. You say "at 300ms add opacity-100, at 500ms remove opacity-0", it runs the sequence asynchronously against a real DOM element. No animation runtime, no state machine to wire up β€” you already have the CSS, this just toggles the classes for you at the right moments.

Plays nicely with utility-class frameworks like Tailwind, where the transitions live in the classes and all you need is something to flip them on cue.

πŸ–€ Features

  • use_animate! β€” one declarative macro, your whole sequence reads top-to-bottom like keyframes
  • add / remove β€” the only two verbs you need; classes go on, classes come off
  • Grouped ops β€” fire several class changes at the exact same tick with (...)
  • Async under the hood β€” sequences run on Dioxus' task runtime, nothing blocks
  • Two ways to target β€” by mounted element reference, or by plain element id

Table of Contents

βΈ»

πŸ“¦ Installation

Add it to your Cargo.toml:

[dependencies]
dioxus-animate = "0.3"

Or let cargo do the editing:

cargo add dioxus-animate

The latest version targets Dioxus 0.7 and the web (WASM) renderer β€” see the compatibility table for the version mapping. Built on the Rust 2024 edition, so you'll want a recent stable toolchain.

βΈ»

πŸ§ͺ Usage

Basic animation sequence

Reach for the use_animate! macro to lay out timed CSS class operations, then call start on a mounted element:

use dioxus::prelude::*;
use dioxus_animate::prelude::*;

#[component]
fn App() -> Element {
    let mut element_ref = use_signal(|| None);

    let animation = use_animate!(
        300 => add("opacity-100"),
        500 => remove("opacity-0"),
        1000 => add("scale-110"),
    );

    let start_animation = move |_| {
        animation.start(element_ref.into());
    };

    rsx! {
        div {
            class: "opacity-0 transition-all duration-300",
            onmounted: move |event| element_ref.set(Some(event.data())),
            onclick: start_animation,
            "Click me to animate!"
        }
    }
}

Grouped operations

Wrap operations in parentheses (separated by ;) to fire them on the same tick:

let animation = use_animate!(
    0 => add("animate-pulse"),
    500 => (
        add("bg-blue-500");
        remove("bg-gray-200")
    ),
    1000 => remove("animate-pulse"),
);

Complex sequences

Chain as many steps as you like β€” single ops and groups mix freely:

let animation = use_animate!(
    0 => add("opacity-100"),
    200 => remove("opacity-0"),
    400 => add("scale-105"),
    600 => (
        add("rotate-3");
        add("shadow-lg")
    ),
    1000 => remove("scale-105 rotate-3"),
    1200 => add("scale-100"),
);

One thing to keep in mind: timestamps are cumulative from the start and must climb in ascending order (same as you'd write CSS keyframes). The runtime sleeps for the gap between each step, so a step that goes backwards in time isn't a thing.

Trigger via element reference

Capture the element on onmounted, then hand its reference to start:

// grab it when the node mounts
onmounted: move |event| element_ref.set(Some(event.data())),

// fire it from any handler
animation.start(element_ref.into());

Trigger via element id

Don't want to juggle references? Target by id with start_for_id instead β€” handy when the element lives somewhere awkward to thread a signal to:

let animation = use_animate!(
    300 => add("opacity-100"),
    500 => remove("opacity-0"),
);

let trigger_animation = move |_| {
    animation.start_for_id("target");
};

rsx! {
    div {
        id: "target",
        class: "opacity-0 transition-all duration-300",
        onclick: trigger_animation,
        "Click me to animate!"
    }
}

Heads up: start_for_id expects the element to exist in the DOM at call time β€” it looks the node up by id and will panic if there's nothing there, so trigger it after the element has mounted.

βΈ»

🧠 How It Works

  1. Define β€” use_animate! parses your time => operation lines into an ordered list of (ms, Operation) pairs
  2. Mount β€” capture the element reference via onmounted (or skip it and target by id)
  3. Trigger β€” start(...) / start_for_id(...) spawns an async task on Dioxus' runtime
  4. Execute β€” the task sleeps to each timestamp in turn and toggles the classes on the live DOM element

Time values are in milliseconds, cumulative from the start of the sequence.

βΈ»

πŸ“ API Reference

use_animate!

Builds an animation sequence:

use_animate!(
    time_ms => operation,
    time_ms => operation,
    // ...
);

Operations:

  • add("class-names") β€” adds CSS classes (space-separated string, multiple classes welcome)
  • remove("class-names") β€” removes CSS classes (same deal)
  • (op1; op2; ...) β€” groups operations to run on the same tick

Time values:

  • expressed in milliseconds
  • cumulative from animation start
  • must be in ascending order (think CSS keyframes)

UseAnimate::start

animation.start(element_ref.into());

Runs the sequence against a mounted element. Takes a ReadSignal<Option<Rc<MountedData>>> β€” in practice the Signal you filled on onmounted, with .into(). If the signal is still None, the call is a no-op (it just won't animate).

UseAnimate::start_for_id

animation.start_for_id("my-element");

Runs the sequence against the element with the given id. Convenient when you'd rather not hold a reference β€” just make sure the element is in the DOM when you call it (it panics if the id isn't found).

βΈ»

πŸ› οΈ Compatibility

Dioxus version dioxus-animate version
0.7 0.3
0.6 0.2

A couple of things worth knowing:

  • Web / WASM only β€” it reaches for web-sys, gloo and Dioxus' web event APIs, so it runs in the browser renderer (it isn't wired up for desktop/mobile).
  • Rust 2024 edition β€” you'll want a recent stable toolchain.

βΈ»

πŸ“ Repo & Contributions

πŸ› οΈ Repo: https://github.com/dsplce-co/dioxus-animate πŸ“¦ Crate: https://crates.io/crates/dioxus-animate

Contributions, issues, ideas? Hit us up πŸ–€

βΈ»

πŸ“„ License

MIT or Apache-2.0, at your option.