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-functionanimation-durationanimation-delaytransition-timing-functiontransition-durationtransition-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.