Motion components
Motion components are DOM primitives optimised for 60fps animation and gestures.
There's a motion
component for every HTML and SVG element, for instance motion.div
, motion.circle
etc.
These work exactly like their static counterparts, but offer props that allow you to:
- Animate via a simple prop.
- Add drag, pan, hover and tap gestures.
- Respond to gestures with animations.
- Deeply animate throughout React trees via variants.
#Supported values
#Value types
Motion can animate:
- Numbers:
0
,10
etc. - Strings containing numbers:
"0vh"
,"10px"
etc. - Colors: Hex, RGB, HSLA.
- Complex strings containing multiple numbers and/or colors (ie
"10px 0 #000"
)
When animating to a non-animatable value like "block"
, this value will be set instantly. By setting this value within transitionEnd
, this value will be set at the end of the animation.
<motion.div animate={{ x: 0, backgroundColor: "#000", boxShadow: "10px 10px 0 rgba(0, 0, 0, 0.2)", position: "fixed", transitionEnd: { display: "none", }, }}/>
#Value type conversion
In general, values can only be animated between two of the same type (ie two px
, two %
etc).
However, HTML component's x
, y
, width
, height
, top
, left
, right
and bottom
values have enhanced support and can all be animated freely between different value types.
<motion.div initial={{ x: "100%" }} animate={{ x: "calc(100vw - 50%)" }}/>
Additionally, any color type (hex, HSL, RGB) can animate between each other.
#Transform
Transform properties are accelerated by the GPU, and therefore animate smoothly. They can be set and animated individually as:
- Translate shortcuts:
x
,y
,z
- Translate:
translateX
,translateY
,translateZ
- Scale:
scale
,scaleX
,scaleY
- Rotate:
rotate
,rotateX
,rotateY
,rotateZ
- Skew:
skew
,skewX
,skewY
- Perspective:
transformPerspective
motion
components have enhanced style
props, allowing you to set them individually there, too.
<motion.a whileHover={{ scale: 1.2 }} whileTap={{ scale: 0.8 }} style={{ x: 100 }}/>
For convenience, transform values are applied in a specific order: translate, scale, rotate, skew.
However, you can customize this default order using the transformTemplate
prop.
function template({ rotate, x }) { return `rotate(${rotate}) translateX(${x})`}
return ( <motion.div transformTemplate={template} animate={{ rotate: 360 }} style={{ rotate: 0, x: "calc(50vh - 100px)" }} />)
SVG note: For SVG components, x
and y
attributes (as opposed to the transform
style) can be set using attrX
and attrY
within animation props.
#Transform origin
transform-origin
has three shortcut values that can be set and animated individually:
originX
originY
originZ
If set as numbers, originX
and Y
default to a progress value between 0
and 1
. originZ
defaults to pixels.
<motion.div style={{ originX: 0.5 }} />
#CSS variables
Motion can animate the value of CSS variables, and also read CSS variables as animation targets.
#Using pre-defined CSS variables in animation
HTML motion
components can animate to and from CSS variables, as long as that variable is defined on a component ancestor.
<motion.li animate={{ background: "var(--action)" }} />
#Animating CSS variables
By defining and animating CSS variables, we can use a parent motion
component to declaratively animate multiple DOM children.
When animating CSS variables in TypeScript, the prop will need to be cast as any
to prevent type errors (as there's an infinite number of variable names).
CSS variables are also of an arbitary type, so Motion can't infer their default type. You're able to animate rotate
as a number because Motion understands that it should be set as deg
, whereas '--rotate'
needs to be explicitly set with the unit type, e.g. '360deg'
.
<motion.ul initial={{ '--rotate': '0deg' } as any} animate={{ '--rotate': '360deg' } as any} transition={{ duration: 2, repeat: Infinity }}> <li style={{ transform: 'rotate(var(--rotate))' }} /> <li style={{ transform: 'rotate(var(--rotate))' }} /> <li style={{ transform: 'rotate(var(--rotate))' }} /></motion.ul>
#SVG line drawing
Line drawing animations can be created with many SVG elements using three special properties: pathLength
, pathSpacing
and pathOffset
.
These are all set as a value between 0
and 1
, where 1
is the measured length of the path.
Path animations are compatible with circle
, ellipse
, line
, path
, polygon
, polyline
and rect
elements.
#Custom components
Any component can be turned into a motion
component by wrapping it with the motion()
function.
The provided component must forward ref
to the DOM element you want to animate.
In addition, motion()
must not be called inside a React render function.
const Component = React.forwardRef((props, ref) => ( <div ref={ref} />))
const MotionComponent = motion(Component)
It's also possible to pass strings to motion
, which will create custom DOM elements.
// Will render <custom-element /> into HTMLconst MotionComponent = motion('custom-element')
By default, all motion
props (like animate
etc) are filtered out of the props
forwarded to the provided component. By providing a forwardMotionProps
config, the provided component will receive these props.
motion(Component, { forwardMotionProps: true })
#Performance
Motion animates values outside the React render cycle for increased performance.
Using MotionValues instead of state to update visual properties will also avoid re-renders.
Where possible, animate just transform values and opacity, as they are GPU-accelerated. This way, you can animate hundreds of layers on modern mobile devices.
// GPU accelerated (fast)<motion.div style={{ x: 0 }} animate={{ x: 100 }} />
// CPU drawing (slower)<motion.div style={{ left: 0 }} animate={{ left: 100 }} />
#Server-side rendering
motion
components are fully compatible with server-side rendering, meaning the initial state of a component will be reflected in the server-generated output.
// Server will output `translateX(100px)`<motion.div initial={false} animate={{ x: 100 }} />
#Exceptions
The following SVG values are not currently compatible with server-side rendering: scale
, rotate
, pathLength
, pathOffset
and pathSpacing
.
scale
and rotate
rely on the dynamic calculation of transformOrigin
- originX
and originY
should be set as strings (either px
or %
) to support these server side.
<motion.circle style={{ scale: 2, originX: "100px", originY: "100px" }}/>
path
values rely on the measurement of the overall path length. Setting strokeDasharray
to "0 1"
will hide the path until Motion can measure it.
<motion.path strokeDasharray="0 1" />
#Props
#Animation
#initial: boolean | Target | VariantLabels
Properties, variant label or array of variant labels to start in.
Set to false
to initialise with the values in animate
(disabling the mount animation).
// As values<motion.div initial={{ opacity: 1 }} />
// As variant<motion.div initial="visible" variants={variants} />
// Multiple variants<motion.div initial={["visible", "active"]} variants={variants} />
// As false (disable mount animation)<motion.div initial={false} animate={{ opacity: 0 }} />
#animate: AnimationControls | TargetAndTransition | VariantLabels | boolean
Values to animate to, variant label(s), or AnimationControls
.
// As values<motion.div animate={{ opacity: 1 }} />
// As variant<motion.div animate="visible" variants={variants} />
// Multiple variants<motion.div animate={["visible", "active"]} variants={variants} />
// AnimationControls<motion.div animate={animation} />
#exit: TargetAndTransition | VariantLabels
A target to animate to when this component is removed from the tree.
This component must be the first animatable child of an AnimatePresence
to enable this exit animation.
This limitation exists because React doesn't allow components to defer unmounting until after an animation is complete. Once this limitation is fixed, the AnimatePresence
component will be unnecessary.
import { AnimatePresence, motion } from 'framer-motion'
export const MyComponent = ({ isVisible }) => { return ( <AnimatePresence> {isVisible && ( <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} /> )} </AnimatePresence> )}
#transition: Transition
Default transition. If no transition
is defined in animate
, it will use the transition defined here.
const spring = { type: "spring", damping: 10, stiffness: 100}
<motion.div transition={spring} animate={{ scale: 1.2 }} />
#variants: Variants
Variants allow you to define animation states and organise them by name. They allow you to control animations throughout a component tree by switching a single animate
prop.
Using transition
options like delayChildren
and staggerChildren
, you can orchestrate when children animations play relative to their parent.
After passing variants to one or more motion
component's variants
prop, these variants can be used in place of values on the animate
, initial
, whileFocus
, whileTap
and whileHover
props.
const variants = { active: { backgroundColor: "#f00" }, inactive: { backgroundColor: "#fff", transition: { duration: 2 } }}
<motion.div variants={variants} animate="active" />
#style: MotionStyle
The React DOM style
prop, enhanced with support for MotionValue
s and separate transform
values.
export const MyComponent = () => { const x = useMotionValue(0)
return <motion.div style={{ x, opacity: 1, scale: 0.5 }} />}
#Layout animation
#layout: boolean | "position" | "size"
If true
, this component will automatically animate to its new position when its layout changes.
This will perform a layout animation using performant transforms. Part of this technique involved animating an element's scale. This can introduce visual distortions on children, boxShadow
and borderRadius
.
To correct distortion on immediate children, add layout
to those too.
boxShadow
and borderRadius
will automatically be corrected if they are already being animated on this component. Otherwise, set them directly via the initial
prop.
If layout
is set to "position"
, only its position will animate. This is good for text components that don't often look good when stretched.
If layout
is set to "size"
, only its size will animate.
#layoutId: string
When a component with a layoutId is removed from the React tree, and then added elsewhere, it will visually animate from the previous component's bounding box and its latest animated values.
If the previous component remains in the tree it will crossfade to the new one.
{items.map(item => ( <motion.li layout> {item.name} {item.isSelected && <motion.div layoutId="underline" />} </motion.li>))}
#layoutDependency: any
Layout changes are, by default, detected every render. To improve performance, you can hint when layout changes are likely to take place by passing a value to layoutDependency
.
The motion
component will then only measure itself when the passed value changes, or if the layoutDependency
of another motion
component within the same LayoutGroup
changes.
#layoutScroll: boolean
For layout animations to work correctly within scrollable elements, their scroll offset needs measuring. For performance reasons, Framer Motion doesn't measure the scroll offset of every ancestor. Add the layoutScroll
prop to elements that should be measured.
<motion.div layoutScroll style={{ overflow: "scroll" }}> <motion.div layout /></motion.div>
#onLayoutAnimationStart(): void
A callback that will fire when a layout animation on this component starts.
#onLayoutAnimationComplete(): void
A callback that will fire when a layout animation on this component completes.
#Animation events
#onUpdate(latest): void
Callback with latest motion values, fired max once per frame.
latest: ResolvedValues
function onUpdate(latest) { console.log(latest.x, latest.opacity)}
<motion.div animate={{ x: 100, opacity: 0 }} onUpdate={onUpdate} />
#onAnimationStart(): void
Callback when animation defined in animate
begins.
function onStart() { console.log("Animation started")}
<motion.div animate={{ x: 100 }} onAnimationStart={onStart} />
#onAnimationComplete(definition): void
Callback when animation defined in animate
is complete.
The provided callback will be called the triggering animation definition. If this is a variant, it'll be the variant name, and if a target object then it'll be the target object.
This way, it's possible to figure out which animation has completed.
definition: AnimationDefinition
function onComplete() { console.log("Animation completed")}
<motion.div animate={{ x: 100 }} onAnimationComplete={definition => { console.log('Completed animating', definition) }}/>
#Gestures
Motion extends the basic set of event listeners provided by React with a simple yet powerful set of UI gesture recognisers.
Learn more
#Advanced
#inherit: boolean
Set to false
to prevent inheriting variant changes from its parent.
#custom: any
Custom data to use to resolve dynamic variants differently for each animating component.
const variants = { visible: (custom) => ({ opacity: 1, transition: { delay: custom * 0.2 } })}
<motion.div custom={0} animate="visible" variants={variants} /><motion.div custom={1} animate="visible" variants={variants} /><motion.div custom={2} animate="visible" variants={variants} />
#transformTemplate(transform, generatedTransform): string
By default, Framer Motion generates a transform
property with a sensible transform order. transformTemplate
can be used to create a different order, or to append/preprend the automatically generated transform
property.
transform: TransformProperties
The latest animated transform props
generatedTransform: string
The transform string as automatically generated by Framer Motion
returns: string
<motion.div style={{ x: 0, rotate: 180 }} transformTemplate={ ({ x, rotate }) => `rotate(${rotate}deg) translateX(${x}px)` }/>