Motion primitive

A motion primitive for animating the entry and exit of components.

Early Access

The new Motion primitive and tokens are currently in early access. We may make frequent breaking changes with limited notice. We don't recommend using this without support from our team.

The Motion primitive is the recommended way to apply entry and exit animations in @atlaskit/motion. It renders a wrapper around its children — no render-prop pattern required — making it easy to compose with any React content, including ADS Primitives.

It supports two approaches for defining animations:

  • Motion tokens — pass a pre-defined motion token to enteringAnimation and exitingAnimation for the simplest setup.
  • Custom xcss styles — pass cssMap styles to enteringAnimationXcss and exitingAnimationXcss for full control over animationName, animationDuration, animationTimingFunction, and animationDelay.

Wrap the Motion primitive with <ExitingPersistence> to enable exit animations when elements are removed from the DOM.

Using motion tokens

The simplest way to use the Motion primitive is with pre-defined motion tokens. Pass a motion token to enteringAnimation and exitingAnimation to apply a paired entering and exiting animation.

import { useState } from 'react'; import Button from '@atlaskit/button/default/button'; import { cssMap, jsx } from '@atlaskit/css'; import ExitingPersistence from '@atlaskit/motion/exiting-persistence'; import Motion from '@atlaskit/motion/entering/motion'; import { token } from '@atlaskit/tokens'; import { Block } from '../utils/blocks'; import { Centered, RetryContainer } from '../utils/containers'; const styles = cssMap({ container: { textAlign: 'center', }, centered: { height: '182px', }, }); const MotionPrimitiveTokenExample = (): JSX.Element => { const [isIn, setIsIn] = useState(true); return ( <RetryContainer> <div css={styles.container}> <Button onClick={() => setIsIn((prev) => !prev)}>{isIn ? 'Exit' : 'Enter'}</Button> <Centered css={styles.centered}> <ExitingPersistence appear> {isIn && ( <Motion enteringAnimation={token('motion.blanket.enter')} exitingAnimation={token('motion.blanket.exit')} > <Block /> </Motion> )} </ExitingPersistence> </Centered> </div> </RetryContainer> ); }; export default MotionPrimitiveTokenExample;

Custom animations with keyframe tokens

For more control, use enteringAnimationXcss and exitingAnimationXcss with cssMap styles that set animationName, animationDuration, and animationTimingFunction using motion tokens. Multiple keyframes can be composed together by joining them in animationName (for example, combining a scale and fade animation).

import { useState } from 'react'; import Button from '@atlaskit/button/default/button'; import { cssMap, jsx } from '@atlaskit/css'; import ExitingPersistence from '@atlaskit/motion/exiting-persistence'; import Motion from '@atlaskit/motion/entering/motion'; import { token } from '@atlaskit/tokens'; import { Block } from '../utils/blocks'; import { Centered, RetryContainer } from '../utils/containers'; const styles = cssMap({ container: { textAlign: 'center', }, centered: { height: '182px', }, entering: { animationDuration: token('motion.duration.xlong'), animationTimingFunction: token('motion.easing.out.practical'), animationName: `${token('motion.keyframe.scale.in.medium')}, ${token('motion.keyframe.fade.in')}`, }, exiting: { animationDuration: token('motion.duration.long'), animationTimingFunction: token('motion.easing.in.practical'), animationName: `${token('motion.keyframe.scale.out.medium')}, ${token('motion.keyframe.fade.out')}`, }, }); const MotionPrimitiveCustomExample = (): JSX.Element => { const [isIn, setIsIn] = useState(true); return ( <RetryContainer> <div css={styles.container}> <Button onClick={() => setIsIn((prev) => !prev)}>{isIn ? 'Exit' : 'Enter'}</Button> <Centered css={styles.centered}> <ExitingPersistence appear> {isIn && ( <Motion enteringAnimationXcss={styles.entering} exitingAnimationXcss={styles.exiting}> <Block /> </Motion> )} </ExitingPersistence> </Centered> </div> </RetryContainer> ); }; export default MotionPrimitiveCustomExample;

Custom keyframes

For animations not covered by the built-in keyframe tokens, define your own CSS keyframes using keyframes() from @compiled/react and reference them directly in the animationName property of a cssMap style alongside token keyframes.

import { useState } from 'react'; import { keyframes } from '@compiled/react'; import Button from '@atlaskit/button/default/button'; import { cssMap, jsx } from '@atlaskit/css'; import ExitingPersistence from '@atlaskit/motion/exiting-persistence'; import Motion from '@atlaskit/motion/entering/motion'; import { token } from '@atlaskit/tokens'; import { Block } from '../utils/blocks'; import { Centered, RetryContainer } from '../utils/containers'; const slideIn = keyframes({ '0%': { transform: 'translateX(-24px)' }, '100%': { transform: 'translateX(0)' }, }); const slideOut = keyframes({ '0%': { transform: 'translateX(0)' }, '100%': { transform: 'translateX(-24px)' }, }); const styles = cssMap({ container: { textAlign: 'center', }, centered: { height: '182px', }, entering: { animationDuration: token('motion.duration.xxlong'), animationTimingFunction: token('motion.easing.out.practical'), animationName: `${slideIn}, ${token('motion.keyframe.fade.in')}`, }, exiting: { animationDuration: token('motion.duration.xxlong'), animationTimingFunction: token('motion.easing.in.practical'), animationName: `${slideOut}, ${token('motion.keyframe.fade.out')}`, }, }); const MotionPrimitiveCustomKeyframeExample = (): JSX.Element => { const [isIn, setIsIn] = useState(true); return ( <RetryContainer> <div css={styles.container}> <Button onClick={() => setIsIn((prev) => !prev)}>{isIn ? 'Exit' : 'Enter'}</Button> <Centered css={styles.centered}> <ExitingPersistence appear> {isIn && ( <Motion enteringAnimationXcss={styles.entering} exitingAnimationXcss={styles.exiting}> <Block /> </Motion> )} </ExitingPersistence> </Centered> </div> </RetryContainer> ); }; export default MotionPrimitiveCustomKeyframeExample;
Was this page helpful?
We use this feedback to improve our documentation.
  • Design system
    • Get started
    • Foundations
    • Components
    • Rovo UI
    • Tools
    • Release phases
    • Contact us
© 2026 AtlassianTrademark, (opens new window)Privacy, (opens new window)License