Transition

Animate presence of component with pre-made animations

PackageIcon

Usage

The Transition component is designed to animate the presence of elements with fixed or absolute positioning, such as dropdowns, modals, or tooltips. Other Mantine components (like Modal and Tooltip) use Transition internally for their animations.

Note that the Transition component is not intended to be a comprehensive solution for all animations. It is a simple utility for animating the presence of elements with fixed or absolute positioning. If you need to implement more complex animations, consider using Motion, React Spring, or other dedicated animation libraries.

Example usage of the Transition component:

import { Transition } from '@mantine/core';

function Demo({ opened }: { opened: boolean }) {
  return (
    <Transition mounted={opened} transition="fade">
      {(styles) => <div style={styles}>Your modal</div>}
    </Transition>
  );
}

Premade transitions

Mantine includes several premade transitions:

fade
fade-up
fade-down
fade-left
fade-right
scale
scale-y
scale-x
skew-up
skew-down
rotate-left
rotate-right
slide-down
slide-up
slide-left
slide-right
pop
pop-bottom-left
pop-bottom-right
pop-top-left
pop-top-right

To use one of them, set the transition property to one of these values:

import { Transition } from '@mantine/core';

function Demo({ opened }: { opened: boolean }) {
  return (
    <Transition
      mounted={opened}
      transition="fade"
      duration={400}
      timingFunction="ease"
    >
      {(styles) => <div style={styles}>Your modal</div>}
    </Transition>
  );
}

Custom transitions

You can create your own transition. transition is an object with 4 properties:

  • in – styles for mounted state
  • out – styles for unmounted state
  • common (optional) – styles for both mounted and unmounted states
  • transitionProperty – properties which participate in the transition
import { useState } from 'react';
import { useClickOutside } from '@mantine/hooks';
import { Transition, Paper, Button, Box } from '@mantine/core';

const scaleY = {
  in: { opacity: 1, transform: 'scaleY(1)' },
  out: { opacity: 0, transform: 'scaleY(0)' },
  common: { transformOrigin: 'top' },
  transitionProperty: 'transform, opacity',
};

function Demo() {
  const [opened, setOpened] = useState(false);
  const clickOutsideRef = useClickOutside(() => setOpened(false));

  return (
    <Box
      maw={200}
      pos="relative"
      style={{ display: 'flex', justifyContent: 'center', margin: 'auto' }}
    >
      <Button onClick={() => setOpened(true)}>Open dropdown</Button>
      <Transition
        mounted={opened}
        transition={scaleY}
        duration={200}
        timingFunction="ease"
        keepMounted
      >
        {(transitionStyle) => (
          <Paper
            shadow="md"
            p="xl"
            h={120}
            pos="absolute"
            top={0}
            left={0}
            right={0}
            ref={clickOutsideRef}
            style={{ ...transitionStyle, zIndex: 1 }}
          >
            Dropdown
          </Paper>
        )}
      </Transition>
    </Box>
  );
}

Enter and exit delay

Use the enterDelay and exitDelay props to delay the transition start. Values are in milliseconds:

import { useState } from 'react';
import { Button, Flex, Paper, Transition } from '@mantine/core';

export function Demo() {
  const [opened, setOpened] = useState(false);

  return (
    <Flex maw={200} pos="relative" justify="center" m="auto">
      <Button onClick={() => setOpened(true)}>Open dropdown</Button>

      <Transition mounted={opened} transition="pop" enterDelay={500} exitDelay={300}>
        {(transitionStyle) => (
          <Paper
            shadow="md"
            p="xl"
            h={120}
            pos="absolute"
            inset={0}
            bottom="auto"
            onClick={() => setOpened(false)}
            style={{ ...transitionStyle, zIndex: 1 }}
          >
            Click to close
          </Paper>
        )}
      </Transition>
    </Flex>
  );
}

Reduced motion

Transition respects the prefers-reduced-motion media query and your theme's respectReducedMotion setting. When reduced motion is preferred, all transitions complete instantly:

import { createTheme, MantineProvider, Transition } from '@mantine/core';

const theme = createTheme({
  respectReducedMotion: true, // default
});

function Demo() {
  return (
    <MantineProvider theme={theme}>
      <Transition mounted transition="fade" duration={400}>
        {(styles) => <div style={styles}>Content</div>}
      </Transition>
    </MantineProvider>
  );
}

This improves accessibility for users with vestibular disorders who may experience motion sickness from animations.

Lifecycle callbacks

Use lifecycle callbacks to perform actions at different stages of the transition:

  • onEnter - Called when enter transition starts
  • onEntered - Called when enter transition completes
  • onExit - Called when exit transition starts
  • onExited - Called when exit transition completes
import { Transition } from '@mantine/core';

function Demo() {
  return (
    <Transition
      mounted
      transition="fade"
      duration={200}
      onEnter={() => console.log('Enter started')}
      onEntered={() => console.log('Enter completed')}
      onExit={() => console.log('Exit started')}
      onExited={() => console.log('Exit completed')}
    >
      {(styles) => <div style={styles}>Content</div>}
    </Transition>
  );
}

Keep mounted

By default, the element is unmounted from the DOM when the transition is complete. Use keepMounted to keep the element mounted with display: none:

import { Transition } from '@mantine/core';

function Demo() {
  return (
    <Transition mounted={false} keepMounted transition="fade">
      {(styles) => <div style={styles}>Content</div>}
    </Transition>
  );
}

This is useful when you want to:

  • Preserve element state during hide/show
  • Avoid remounting overhead
  • Maintain focus/scroll position