📄 radixui/primitives/docs/guides/animation

File: animation.md | Updated: 11/15/2025

Source: https://www.radix-ui.com/primitives/docs/guides/animation

Radix Homepage

Made by WorkOS

Radix Homepage

Made by WorkOS

ThemesThemes PrimitivesPrimitives IconsIcons ColorsColors

Documentation Case studies Blog

Search

/

Overview

Introduction Getting started Accessibility Releases

Guides

Styling Animation Composition Server-side rendering

Components

Accordion Alert Dialog Aspect Ratio Avatar Checkbox Collapsible Context Menu Dialog Dropdown Menu Form

Preview
Hover Card Label Menubar Navigation Menu One-Time Password Field

Preview
Password Toggle Field

Preview
Popover Progress Radio Group Scroll Area Select Separator Slider Switch Tabs Toast Toggle Toggle Group Toolbar Tooltip

Utilities

Accessible Icon Direction Provider Portal Slot Visually Hidden

Guides

Animation

Animate Radix Primitives with CSS keyframes or the JavaScript animation library of your choice.

Adding animation to Radix Primitives should feel similar to any other component, but there are some caveats noted here in regards to exiting animations with JS animation libraries.

Animating with CSS animation


The simplest way to animate Primitives is with CSS.

You can use CSS animation to animate both mount and unmount phases. The latter is possible because the Radix Primitives will suspend unmount while your animation plays out.

@keyframes fadeIn {
	from {
		opacity: 0;
	}
	to {
		opacity: 1;
	}
}

@keyframes fadeOut {
	from {
		opacity: 1;
	}
	to {
		opacity: 0;
	}
}

.DialogOverlay[data-state="open"],
.DialogContent[data-state="open"] {
	animation: fadeIn 300ms ease-out;
}

.DialogOverlay[data-state="closed"],
.DialogContent[data-state="closed"] {
	animation: fadeOut 300ms ease-in;
}

Delegating unmounting for JavaScript Animation


When many stateful Primitives are hidden from view, they are actually removed from the React Tree, and their elements removed from the DOM. JavaScript animation libraries need control of the unmounting phase, so we provide the forceMount prop on many components to allow consumers to delegate the mounting and unmounting of children based on the animation state determined by those libraries.

For example, if you want to use React Spring to animate a Dialog, you would do so by conditionally rendering the dialog Overlay and Content parts based on the animation state from one of its hooks like useTransition:

import { Dialog } from "radix-ui";
import { useTransition, animated, config } from "react-spring";

function Example() {
	const [open, setOpen] = React.useState(false);
	const transitions = useTransition(open, {
		from: { opacity: 0, y: -10 },
		enter: { opacity: 1, y: 0 },
		leave: { opacity: 0, y: 10 },
		config: config.stiff,
	});
	return (
		<Dialog.Root open={open} onOpenChange={setOpen}>
			<Dialog.Trigger>Open Dialog</Dialog.Trigger>
			{transitions((styles, item) =>
				item ? (
					<>
						<Dialog.Overlay forceMount asChild>
							<animated.div
								style={{
									opacity: styles.opacity,
								}}
							/>
						</Dialog.Overlay>
						<Dialog.Content forceMount asChild>
							<animated.div style={styles}>
								<h1>Hello from inside the Dialog!</h1>
								<Dialog.Close>close</Dialog.Close>
							</animated.div>
						</Dialog.Content>
					</>
				) : null,
			)}
		</Dialog.Root>
	);
}

PreviousStyling

NextComposition

Edit this page on GitHub.