> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/radix-ui/primitives/llms.txt
> Use this file to discover all available pages before exploring further.

# Animation

> Learn how to animate Radix UI Primitives using CSS transitions, keyframe animations, and animation libraries

Radix Primitives are built with animation in mind. Components expose data attributes that make it easy to target different states, and the Presence component handles exit animations.

## Data State Attributes

Most interactive Radix components provide `data-state` attributes that reflect their current state. You can use these to apply animations:

```tsx theme={null}
import * as Dialog from '@radix-ui/react-dialog';
import './styles.css';

function MyDialog() {
  return (
    <Dialog.Root>
      <Dialog.Trigger>Open</Dialog.Trigger>
      <Dialog.Portal>
        <Dialog.Overlay className="dialog-overlay" />
        <Dialog.Content className="dialog-content">
          <Dialog.Title>Animated Dialog</Dialog.Title>
          <Dialog.Close>Close</Dialog.Close>
        </Dialog.Content>
      </Dialog.Portal>
    </Dialog.Root>
  );
}
```

## CSS Transitions

Use CSS transitions with data attributes for simple animations:

```css filename="styles.css" theme={null}
.dialog-overlay {
  background-color: rgba(0, 0, 0, 0.5);
  position: fixed;
  inset: 0;
  transition: opacity 150ms ease-in-out;
}

.dialog-overlay[data-state='open'] {
  opacity: 1;
}

.dialog-overlay[data-state='closed'] {
  opacity: 0;
}

.dialog-content {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: white;
  border-radius: 8px;
  padding: 24px;
  transition: all 200ms ease-in-out;
}

.dialog-content[data-state='open'] {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}

.dialog-content[data-state='closed'] {
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.95);
}
```

<Note>
  Components expose `data-state` with values like `"open"`, `"closed"`, `"checked"`, `"unchecked"`, etc.
</Note>

## CSS Keyframe Animations

Use CSS keyframes for more complex animations:

```css filename="styles.css" theme={null}
@keyframes slideDown {
  from {
    opacity: 0;
    transform: translateY(-12px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes slideUp {
  from {
    opacity: 1;
    transform: translateY(0);
  }
  to {
    opacity: 0;
    transform: translateY(-12px);
  }
}

.dropdown-content[data-state='open'] {
  animation: slideDown 200ms ease-out;
}

.dropdown-content[data-state='closed'] {
  animation: slideUp 150ms ease-in;
}
```

### Example with Accordion

```tsx theme={null}
import * as Accordion from '@radix-ui/react-accordion';
import './accordion.css';

function AnimatedAccordion() {
  return (
    <Accordion.Root type="single" collapsible>
      <Accordion.Item value="item-1">
        <Accordion.Header>
          <Accordion.Trigger className="accordion-trigger">
            What is Radix?
          </Accordion.Trigger>
        </Accordion.Header>
        <Accordion.Content className="accordion-content">
          Radix is an open-source UI component library.
        </Accordion.Content>
      </Accordion.Item>
    </Accordion.Root>
  );
}
```

```css filename="accordion.css" theme={null}
.accordion-content {
  overflow: hidden;
}

.accordion-content[data-state='open'] {
  animation: slideDown 300ms cubic-bezier(0.87, 0, 0.13, 1);
}

.accordion-content[data-state='closed'] {
  animation: slideUp 300ms cubic-bezier(0.87, 0, 0.13, 1);
}

@keyframes slideDown {
  from {
    height: 0;
  }
  to {
    height: var(--radix-accordion-content-height);
  }
}

@keyframes slideUp {
  from {
    height: var(--radix-accordion-content-height);
  }
  to {
    height: 0;
  }
}
```

<Note>
  Radix provides CSS variables like `--radix-accordion-content-height` and `--radix-collapsible-content-width` for animating dynamic dimensions.
</Note>

## Exit Animations

Radix uses the `Presence` component internally to handle exit animations. The component stays mounted while animating out, allowing CSS animations to complete.

### Force Mount for Animation Control

For advanced animation control, use the `forceMount` prop:

```tsx theme={null}
import * as Dialog from '@radix-ui/react-dialog';

function ControlledDialog() {
  return (
    <Dialog.Root>
      <Dialog.Trigger>Open</Dialog.Trigger>
      <Dialog.Portal forceMount>
        <Dialog.Overlay forceMount />
        <Dialog.Content forceMount>
          Content
        </Dialog.Content>
      </Dialog.Portal>
    </Dialog.Root>
  );
}
```

<Warning>
  When using `forceMount`, you're responsible for handling the component's presence and animations manually.
</Warning>

## Animation Libraries

### Framer Motion

Use Framer Motion with the `asChild` prop for advanced animations:

```tsx theme={null}
import * as Dialog from '@radix-ui/react-dialog';
import { motion, AnimatePresence } from 'framer-motion';

function MotionDialog() {
  const [open, setOpen] = useState(false);

  return (
    <Dialog.Root open={open} onOpenChange={setOpen}>
      <Dialog.Trigger>Open</Dialog.Trigger>
      <AnimatePresence>
        {open && (
          <Dialog.Portal forceMount>
            <Dialog.Overlay asChild forceMount>
              <motion.div
                initial={{ opacity: 0 }}
                animate={{ opacity: 1 }}
                exit={{ opacity: 0 }}
                transition={{ duration: 0.2 }}
              />
            </Dialog.Overlay>
            <Dialog.Content asChild forceMount>
              <motion.div
                initial={{ opacity: 0, scale: 0.95, y: 20 }}
                animate={{ opacity: 1, scale: 1, y: 0 }}
                exit={{ opacity: 0, scale: 0.95, y: 20 }}
                transition={{ duration: 0.2 }}
              >
                <Dialog.Title>Animated with Framer Motion</Dialog.Title>
                <Dialog.Close>Close</Dialog.Close>
              </motion.div>
            </Dialog.Content>
          </Dialog.Portal>
        )}
      </AnimatePresence>
    </Dialog.Root>
  );
}
```

<Steps>
  <Step title="Install Framer Motion">
    ```bash theme={null}
    npm install framer-motion
    ```
  </Step>

  <Step title="Use controlled state">
    Control the `open` state to work with `AnimatePresence`:

    ```tsx theme={null}
    const [open, setOpen] = useState(false);
    ```
  </Step>

  <Step title="Add forceMount">
    Use `forceMount` to keep the component mounted during animations:

    ```tsx theme={null}
    <Dialog.Content asChild forceMount>
    ```
  </Step>

  <Step title="Wrap with motion components">
    Use `motion.div` with `asChild` to apply Framer Motion animations:

    ```tsx theme={null}
    <Dialog.Content asChild forceMount>
      <motion.div {...motionProps}>
        {/* content */}
      </motion.div>
    </Dialog.Content>
    ```
  </Step>
</Steps>

### React Spring

```tsx theme={null}
import * as Collapsible from '@radix-ui/react-collapsible';
import { useSpring, animated } from '@react-spring/web';

function SpringCollapsible() {
  const [open, setOpen] = useState(false);
  
  const styles = useSpring({
    opacity: open ? 1 : 0,
    height: open ? 'auto' : 0,
    config: { tension: 300, friction: 25 },
  });

  return (
    <Collapsible.Root open={open} onOpenChange={setOpen}>
      <Collapsible.Trigger>Toggle</Collapsible.Trigger>
      <Collapsible.Content asChild forceMount>
        <animated.div style={styles}>
          Content with React Spring animation
        </animated.div>
      </Collapsible.Content>
    </Collapsible.Root>
  );
}
```

## Common Animation Patterns

### Slide and Fade

```css theme={null}
@keyframes slideUpAndFade {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

[data-state='open'] {
  animation: slideUpAndFade 200ms ease-out;
}
```

### Scale and Fade

```css theme={null}
@keyframes scaleIn {
  from {
    opacity: 0;
    transform: scale(0.95);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

[data-state='open'] {
  animation: scaleIn 150ms ease-out;
}
```

### Rotate Icon

```css theme={null}
.accordion-trigger[data-state='open'] .icon {
  transform: rotate(180deg);
  transition: transform 200ms ease-in-out;
}
```

## Performance Tips

<Note>
  * Use `transform` and `opacity` for better performance (GPU-accelerated)
  * Avoid animating `width`, `height`, or `margin` when possible
  * Use `will-change` sparingly and remove it after animations complete
  * Test animations on lower-end devices
</Note>

```css theme={null}
/* Good - GPU accelerated */
.dialog-content {
  transition: opacity 200ms, transform 200ms;
}

/* Avoid - forces layout recalculation */
.dialog-content {
  transition: width 200ms, height 200ms;
}
```
