Claude
Skills
Sign in
Back

animations-transitions

Included with Lifetime
$97 forever

MUI transition components (Fade, Grow, Slide, Collapse, Zoom), custom transitions, TransitionGroup, and Framer Motion integration

General

What this skill does


# MUI Animations & Transitions

## 1. Built-in Transition Components

MUI provides five transition components built on top of `react-transition-group`. Each wraps a single child element and controls its enter/exit animation based on the `in` prop.

### Fade

Opacity transition from transparent to opaque.

```tsx
import { Fade, Box, Button } from '@mui/material';
import { useState } from 'react';

function FadeExample() {
  const [visible, setVisible] = useState(false);

  return (
    <>
      <Button onClick={() => setVisible((v) => !v)}>Toggle</Button>
      <Fade in={visible} timeout={300}>
        <Box sx={{ p: 2, bgcolor: 'primary.main', color: 'white' }}>
          Fading content
        </Box>
      </Fade>
    </>
  );
}
```

**Key props:**

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `in` | `boolean` | `false` | Controls visibility |
| `timeout` | `number \| { enter, exit }` | `300` | Duration in ms |
| `appear` | `boolean` | `true` | Animate on initial mount when `in` is true |
| `unmountOnExit` | `boolean` | `false` | Remove DOM node when exited |
| `mountOnEnter` | `boolean` | `false` | Defer first mount until `in` is true |

### Grow

Combined scale and opacity transition. The element grows from the center (or a custom origin) while fading in.

```tsx
import { Grow, Paper } from '@mui/material';

function GrowExample({ visible }: { visible: boolean }) {
  return (
    <Grow
      in={visible}
      timeout={500}
      style={{ transformOrigin: '0 0 0' }}
      {...(visible ? { timeout: 1000 } : {})}
    >
      <Paper elevation={4} sx={{ p: 3, width: 200 }}>
        Growing content
      </Paper>
    </Grow>
  );
}
```

**Custom transform origin:**

```tsx
<Grow in={open} style={{ transformOrigin: 'center top' }}>
  <MenuContent />
</Grow>
```

### Slide

Directional slide transition. The element slides in from an edge of the screen or a container.

```tsx
import { Slide, Paper } from '@mui/material';
import { useRef } from 'react';

function SlideExample({ visible }: { visible: boolean }) {
  const containerRef = useRef<HTMLDivElement>(null);

  return (
    <Box ref={containerRef} sx={{ overflow: 'hidden', height: 200 }}>
      <Slide
        direction="up"
        in={visible}
        container={containerRef.current}
        timeout={{ enter: 400, exit: 200 }}
      >
        <Paper sx={{ p: 2 }}>Slides up into view</Paper>
      </Slide>
    </Box>
  );
}
```

**`direction` values:** `'up'` | `'down'` | `'left'` | `'right'`

When `container` is provided, the element slides relative to that container instead of the viewport.

### Collapse

Height (or width) animation that expands/collapses content.

```tsx
import { Collapse, List, ListItem, ListItemText, Button, Box } from '@mui/material';

function CollapseExample() {
  const [expanded, setExpanded] = useState(false);

  return (
    <Box>
      <Button onClick={() => setExpanded((e) => !e)}>
        {expanded ? 'Collapse' : 'Expand'}
      </Button>
      <Collapse in={expanded} timeout="auto" unmountOnExit>
        <List>
          <ListItem><ListItemText primary="Item 1" /></ListItem>
          <ListItem><ListItemText primary="Item 2" /></ListItem>
          <ListItem><ListItemText primary="Item 3" /></ListItem>
        </List>
      </Collapse>
    </Box>
  );
}
```

**Horizontal collapse:**

```tsx
<Collapse in={open} orientation="horizontal" collapsedSize={40}>
  <Sidebar />
</Collapse>
```

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `collapsedSize` | `number \| string` | `'0px'` | Minimum size when collapsed (e.g., `40` to keep a peek visible) |
| `orientation` | `'vertical' \| 'horizontal'` | `'vertical'` | Direction of collapse |
| `timeout` | `number \| 'auto'` | `duration.standard` | `'auto'` calculates duration from height |

### Zoom

Scale transition from the center of the child element.

```tsx
import { Zoom, Fab, AddIcon } from '@mui/material';

function ZoomFab({ visible }: { visible: boolean }) {
  return (
    <Zoom in={visible} timeout={200} unmountOnExit>
      <Fab color="primary" sx={{ position: 'fixed', bottom: 16, right: 16 }}>
        <AddIcon />
      </Fab>
    </Zoom>
  );
}
```

---

## 2. Usage Patterns

### Basic Pattern

```tsx
<Fade in={visible} timeout={300}>
  <Box>Content</Box>
</Fade>
```

### Ref Forwarding for Custom Components

MUI transition components pass a `ref` to their child. If the child is a custom component, it must forward the ref:

```tsx
import { forwardRef } from 'react';
import { Fade, FadeProps } from '@mui/material';

// Custom component that forwards ref
const CustomCard = forwardRef<HTMLDivElement, { title: string }>(
  function CustomCard({ title, ...props }, ref) {
    return (
      <div ref={ref} {...props}>
        <h3>{title}</h3>
      </div>
    );
  }
);

// Usage inside a transition
function AnimatedCard({ visible }: { visible: boolean }) {
  return (
    <Fade in={visible}>
      <CustomCard title="Hello" />
    </Fade>
  );
}
```

Without `forwardRef`, the transition will not work and React will warn about refs on function components.

### Conditional Rendering vs Visibility Toggle

**Visibility toggle** (keeps DOM node, hides with CSS):

```tsx
<Fade in={visible}>
  <Box>Always in DOM, opacity changes</Box>
</Fade>
```

**Conditional rendering** (removes DOM node on exit):

```tsx
<Fade in={visible} unmountOnExit mountOnEnter>
  <Box>Removed from DOM when hidden</Box>
</Fade>
```

Use `unmountOnExit` when:
- The hidden content is expensive (heavy components, iframes)
- You need to reset component state on re-entry
- You want to reduce DOM size for accessibility screen readers

Use visibility toggle when:
- You need instant re-show without remount cost
- The component maintains scroll position or form state

---

## 3. TransitionGroup -- Animating Lists

`TransitionGroup` from `react-transition-group` works with MUI transitions to animate items being added or removed from a list.

```tsx
import { TransitionGroup } from 'react-transition-group';
import {
  Collapse,
  List,
  ListItem,
  ListItemText,
  IconButton,
  Button,
  Box,
} from '@mui/material';
import DeleteIcon from '@mui/icons-material/Delete';
import { useState } from 'react';

interface Item {
  id: number;
  name: string;
}

function AnimatedList() {
  const [items, setItems] = useState<Item[]>([
    { id: 1, name: 'Apple' },
    { id: 2, name: 'Banana' },
    { id: 3, name: 'Cherry' },
  ]);

  const addItem = () => {
    const id = Date.now();
    setItems((prev) => [...prev, { id, name: `Item ${id}` }]);
  };

  const removeItem = (id: number) => {
    setItems((prev) => prev.filter((item) => item.id !== id));
  };

  return (
    <Box>
      <Button onClick={addItem} variant="contained" sx={{ mb: 2 }}>
        Add Item
      </Button>
      <List>
        <TransitionGroup>
          {items.map((item) => (
            <Collapse key={item.id}>
              <ListItem
                secondaryAction={
                  <IconButton edge="end" onClick={() => removeItem(item.id)}>
                    <DeleteIcon />
                  </IconButton>
                }
              >
                <ListItemText primary={item.name} />
              </ListItem>
            </Collapse>
          ))}
        </TransitionGroup>
      </List>
    </Box>
  );
}
```

**Using Fade instead of Collapse for list items:**

```tsx
<TransitionGroup>
  {items.map((item) => (
    <Fade key={item.id} timeout={500}>
      <ListItem>
        <ListItemText primary={item.name} />
      </ListItem>
    </Fade>
  ))}
</TransitionGroup>
```

**Important:** Each child of `TransitionGroup` must have a unique `key`. The transition component (Collapse, Fade, etc.) should be the direct child of TransitionGroup, wrapping the actual content.

---

## 4. Custom Transitions

Create reusable transition components using the `Transition` component from `react-transition-group`.

### Using MUI's `styled` + Trans

Related in General