Motion

A set of utilities to apply motion in your application.

How to use the Motion library

Motion primitive

Use the Motion primitive to apply entry and exit animations. It uses semantic enter/exit tokens and base tokens for custom transitions, enabling streamlined implementation without redoing base system behaviors. Pass pre-defined motion tokens to enteringAnimation and exitingAnimation, or supply custom cssMap styles via enteringAnimationXcss and exitingAnimationXcss for full control.

How the primitive is composed

Wrap your component in <Motion> and get best-practice enter/exit transitions out of the box.

import { Motion } from '@atlaskit/motion';

<Motion
    enteringAnimation={token('motion.example.enter')}
    exitingAnimation={token('motion.example.exit')}
>
    <Card />
</Motion>;

How motion components work together

The Motion primitive can be paired with motion StaggeredEntrance and ExitingPersistence components for more detailed choreography.

import { ExitingPersistence, StaggeredEntrance, Motion } from '@atlaskit/motion';
import { token } from '@atlaskit/tokens';

<StaggeredEntrance>
    <ExitingPersistence appear>
        {isIn && (
            <Motion
                enteringAnimationXcss={styles.entering}
                exitingAnimation={styles.exiting}
            >
                <Card appearance="small" />
            </Motion>
            <Motion
                enteringAnimationXcss={styles.entering}
                exitingAnimation={styles.exiting}
            >
                <Card appearance="small" />
            </Motion>
            <Motion
                enteringAnimationXcss={styles.entering}
                exitingAnimation={styles.exiting}
            >
                <Card appearance="small" />
            </Motion>
        )}
    </ExitingPersistence>
</StaggeredEntrance>

useMotion hook

useMotion runs the same entry and exit lifecycle as the Motion primitive, but renders no markup. It returns the current motion state, and you apply the animation to an element you already render.

import { useMotion } from '@atlaskit/motion/use-motion';

const { ref, state } = useMotion<HTMLLIElement>();

<li ref={ref} css={[styles.item, state === 'entering' && styles.entering]}>
    Item
</li>;

Both are the same engine, the Motion primitive is built on useMotion internally, and the primitive is not being deprecated. Use the Motion primitive by default: it is less code and handles the animation styling, the hidden states, and reduced motion for you. Use useMotion when the primitive's wrapper <div> would break something:

  • Layout, where the animated element must stay a direct child of a CSS grid or flex container.
  • Semantics or accessibility, where a parent and child must stay directly related, such as <ul> and <li>, <table> and <tr>, or elements with related ARIA roles.
  • Attributes, where a parent selects on something that must sit on the animated element itself, such as a layout slot marker or an ARIA attribute.
  • Composition, where the animation needs to apply to an element you already render.

The resulting motion is identical either way, so this is an implementation choice rather than a design one. See useMotion for the state machine and the responsibilities you take on with the hook.

CSS over JavaScript

Where possible this library uses CSS exclusively, and only falls back to JavaScript when there is no alternative. The reason for this is primarily for performance. CSS animations run on the compositor thread, avoid blocking the main thread, and execute without waiting for JavaScript to load, important for SSR-rendered apps.

In practice this means:

  • CSS animations and transitions are preferred over animation engines
  • Client-side calculations to drive motion are avoided where CSS can do the same job
  • Spring-style motions are emulated with CSS animation curves
  • Highly interactive or gestural motions may warrant an animation engine, but these are handled case-by-case

For new CSS-based animations that are not entry or exit animations, use motion tokens to ensure consistency, accessibility, and automatic updates across products. Use tokens like motion.duration.* and motion.easing.* to define animation and transition CSS properties:

  • animation-timing-function
  • animation-duration
  • animation-delay
  • transition-timing-function
  • transition-duration
  • transition-delay

For entry and exit transitions, use the Motion primitive, which manages the animation lifecycle using the correct tokens. For the full list of motion tokens, visit All tokens.

Tokens over custom values

Use the provided tokens to power your motion. This ensures a consistent motion experience across all apps. If there is motion you need that doesn't exist yet, reach out to the ADS team for contribution consideration.

Reduced motion support

While motion is used to create relationships, highlight what matters, and create delight, it's equally important to allow users to opt out. Every motion component and custom animation should respect the user's reduced-motion preference. See Accessibility for the utilities available.

Legacy entering-motion components

Caution

FadeIn, SlideIn, ZoomIn and ShrinkOut are legacy components and are planned for deprecation. For new work, use the Motion primitive component instead.

Not rendering markup

The legacy entering motions do not render markup — they pass down props for you to wire up, using the children-as-props pattern:

<FadeIn>
  {props => <div {...props} />}
</FadeIn>

Note that this is not the same as useMotion. The hook's return value is not a set of props to spread onto an element — it returns state for you to map to your own styles, plus a ref and reanimate.

Usage with Primitives

Motion animation components cannot be used to animate Primitive components directly. This is because the className prop needs to be passed to the element used for the animation, and Primitives do not expose this prop.

However, Primitives can still be used within the children of the motion component.

If you need to style the element that the animation is being applied to, an alternative is to use css from @compiled/react:

import type { ReactNode } from 'react';
import { css } from '@compiled/react';
import { SlideIn } from '@atlaskit/motion';
import { Box, Inline } from '@atlaskit/primitives';

const styles = css({
    width: '100vw',
    height: '90dvh',
});

const ComponentWithSlideIn = ({ children }: { children: ReactNode }) => (
    <SlideIn enterFrom="bottom" fade="in">
        {({ className, ref }) => (
            <div css={styles} className={className} ref={ref} aria-modal>
                <Inline>
                    <Heading size="large">Hello!</Heading>
                    <Box>{children}</Box>
                </Inline>
            </div>
        )}
    </SlideIn>
);

If you are maintaining existing code that uses these components, see Entering motions for their full API reference.

Was this page helpful?
We use this feedback to improve our documentation.
© 2026 AtlassianTrademark, (opens new window)Privacy, (opens new window)License