Skip to main content

Animations

GTKX provides a declarative animation API through the x.Animation component, powered by libadwaita's animation system. It supports both timed (duration-based) and spring (physics-based) animations.

Basic Usage

Wrap any widget with x.Animation to animate its properties:

import { x, GtkButton } from "@gtkx/react";

const FadeInButton = () => (
<x.Animation
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ mode: "timed" }}
animateOnMount
>
<GtkButton label="Hello" />
</x.Animation>
);

The initial prop sets the starting values, animate sets the target values, and animateOnMount triggers the animation when the component first renders.

You can animate properties like opacity, translateX, translateY, scale, scaleX, scaleY, rotate, skewX, and skewY. See the AnimatableProperties API reference for the full list.

<x.Animation
initial={{ opacity: 0, scale: 0.8, translateY: -20 }}
animate={{ opacity: 1, scale: 1, translateY: 0 }}
transition={{ mode: "spring" }}
animateOnMount
>
<GtkLabel label="Animated!" />
</x.Animation>

Timed Animations

Timed animations run for a fixed duration with an easing curve:

import { x, GtkBox } from "@gtkx/react";
import { Easing } from "@gtkx/ffi/adw";

const TimedExample = () => (
<x.Animation
initial={{ translateX: -100 }}
animate={{ translateX: 0 }}
transition={{
mode: "timed",
duration: 500,
easing: Easing.EASE_OUT_CUBIC,
delay: 100,
}}
animateOnMount
>
<GtkBox />
</x.Animation>
);

See the TimedTransition API reference for all available options.

Spring Animations

Spring animations use physics simulation for natural-feeling motion:

import { x, GtkButton } from "@gtkx/react";

const SpringExample = () => (
<x.Animation
initial={{ scale: 0.5 }}
animate={{ scale: 1 }}
transition={{
mode: "spring",
damping: 0.6,
stiffness: 200,
mass: 1,
}}
animateOnMount
>
<GtkButton label="Bounce!" />
</x.Animation>
);

See the SpringTransition API reference for all available options.

Animating on Prop Changes

When animate changes, the component automatically transitions to the new values:

import { x, GtkButton, GtkBox } from "@gtkx/react";
import { useState } from "react";

const ToggleAnimation = () => {
const [expanded, setExpanded] = useState(false);

return (
<GtkBox>
<GtkButton
label={expanded ? "Collapse" : "Expand"}
onClicked={() => setExpanded(!expanded)}
/>
<x.Animation
animate={{ scale: expanded ? 1.2 : 1 }}
transition={{ mode: "spring", damping: 0.7, stiffness: 300 }}
>
<GtkButton label="Animated" />
</x.Animation>
</GtkBox>
);
};

Exit Animations

Use the exit prop to animate when the component unmounts:

import { x, GtkButton, GtkBox } from "@gtkx/react";
import { useState } from "react";

const ExitExample = () => {
const [visible, setVisible] = useState(true);

return (
<GtkBox>
<GtkButton label="Toggle" onClicked={() => setVisible(!visible)} />
{visible && (
<x.Animation
initial={{ opacity: 0, translateY: -20 }}
animate={{ opacity: 1, translateY: 0 }}
exit={{ opacity: 0, translateY: 20 }}
transition={{ mode: "timed", duration: 200 }}
animateOnMount
>
<GtkLabel label="I fade in and out!" />
</x.Animation>
)}
</GtkBox>
);
};

The component stays mounted during the exit animation and is removed after it completes.

Animation Callbacks

Monitor animation state with callbacks:

<x.Animation
animate={{ opacity: 1 }}
transition={{ mode: "spring" }}
onAnimationStart={() => console.log("Started")}
onAnimationComplete={() => console.log("Finished")}
animateOnMount
>
<GtkButton label="Watch console" />
</x.Animation>

Skipping Initial Animation

Set initial={false} to skip the initial state and start at the animate values:

<x.Animation
initial={false}
animate={{ opacity: isActive ? 1 : 0.5 }}
transition={{ mode: "timed" }}
>
<GtkButton label="No mount animation" />
</x.Animation>

This is useful when you only want to animate on prop changes, not on mount.