# Migrate from Pose to Motion
How to convert a Pose project to Framer Motion.Framer Motion is the successor to the Pose animation library. Like Pose, it provides a declarative API to power animations and gestures in your React app. But Motion attempts to make the API even simpler for the simplest cases, yet more flexible to handle the most advanced.
In this guide we'll explain how to migrate a Pose project to Motion.
# posed
vs motion
The primary API change is that in Pose, components had to be pre-made outside the render function.
const MyComponent = posed.div(config)
This proved to be very inflexible. In Motion, motion
components are called directly within the render function itself.
<motion.div />
# Poses vs variants
Poses exist in Framer Motion under the name "variants". In Pose, they had to be defined outside of the render function, pre-defined in a component.
// Pose
const Posed = posed.div({
visible: { opacity: 1 },
hidden: { opacity: 0 },
})
const MyComponent = () => {
return <Posed initialPose="hidden" pose="visible" />
}
Variants work in much the same way as poses, only now they can be defined anywhere and passed in via the variants
prop.
// Motion
const MyComponent = () => {
const variants = {
visible: { opacity: 1 },
hidden: { opacity: 0 },
}
return (
<motion.div
initial="hidden"
animate="visible"
variants={variants}
/>
)
}
Of course, in Framer Motion you only have to use variants if you're performing animations throughout the tree. Otherwise you can set props to animate
directly.
<motion.div animate={{ opacity: 0 }} />
# Dynamic poses
Each value within a Pose could be set as a function, to dynamically return a different value based on the props passed to that component.
const Posed = posed.div({
visible: {
opacity: ({ target }) => target,
},
hidden: { opacity: 0 },
})
const MyComponent = () => {
return (
<Posed initialPose="hidden" pose="visible" target={1} />
)
}
This came with the limitation that the pose itself couldn't be dynamic. The values themselves could change, but we couldn't return different poses with different values.
In Motion, because variants can be defined within the render function, it might be that you don't even need dynamic variants. But because the same variants
definition can be used for multiple components, they are often still useful.
So in Motion, entire variants are dynamic, so they can return entirely different definitions based on the data received. This data is now passed through custom
to ensure type safety for the rest of the component's props (as the previous approach required very permissive types).
// Motion
const MyComponent = () => {
const variants = {
visible: target => ({ opacity: target }),
hidden: { opacity: 0 },
}
return (
<motion.div
initial="hidden"
animate="visible"
variants={variants}
custom={1}
/>
)
}
# applyAtStart/applyAtEnd
Pose offered the applyAtStart
and applyAtEnd
for setting properties at the start and end of a transition.
const Posed = posed.div({
visible: {
opacity: 1,
applyAtStart: {
display: "block",
},
},
hidden: {
opacity: 0,
applyAtEnd: {
display: "none",
},
},
})
In Motion, non-animatable properties are automatically applied at the start of an animation. Anything that you want to be applied at the end can be set to transitionEnd
.
const variants = {
visible: {
opacity: 1,
display: "block",
},
hidden: {
opacity: 0,
transitionEnd: {
display: "none",
},
},
}
# Durations
Durations and delays in Pose were set in milliseconds. In Motion, they're set in seconds.
// Pose
const duration = 1000
// Motion
const duration = 1
# Transition
Orchestration props like staggerChildren
have now moved underneath transition
.
const variants = {
visible: {
opacity: 1,
display: "block",
transition: {
staggerChildren: 0.1,
},
},
}
In Pose, there were both beforeChildren
and afterChildren
boolean props. In Motion, there's just one: when
. This can be set to either "beforeChildren"
or "afterChildren"
.
You can also set a default transition
for a component via the transition
prop.
<motion.div transition={{ duration: 2 }} />
# Mount animations
In Pose, to animate a component on mount, you had to set the initialPose
prop.
<PosedComponent initialPose="hidden" pose="visible" />
In Motion, components will always animate on mount if the content of animate
is different to initial
, or the value read from the DOM (as set by CSS).
<motion.div animate={{ scale: 2 }} />
This mount animation can be suppressed by setting initial
to false
.
<motion.div initial={false} animate={{ scale: 2 }} />
Unlike Pose, this initial state will also be rendered server side, so no more flashes of styles.
# Hoverable/Pressable
Pose provided shortcuts to animate a component while certain gestures were active. For instance to respond to hover gestures with an animation, you had to set hoverable
to true
and provide a "hover"
pose.
const PosedComponent = posed.div({
hover: { scale: 1.1 },
hoverable: true,
})
This had a couple of limitations in that the name of the hoverable pose had to be "hover"
. Because of this, we couldn't use the presence of the "hover"
pose to tell whether this specific component should respond to hovers, or we were just defining a pose to animate as a result of a hover over a parent component.
Instead, Motion provides the whileHover
and whileTap
props instead. These can be set either as an object of values, or the name of a variant.
<motion.a whileHover={{ scale: 1.1 }} />
# Draggable
In Pose, a component could be made draggable by setting its config to draggable: true | "x" | "y"
. This was inconvenient because once defined, this configuration couldn't be changed.
posed.div({ draggable: "x" })
In Motion, drag
is a prop that can be changed like any other.
<motion.div drag />
There was also a special "drag"
pose to animate to while dragging was active. But in Motion the whileTap
prop can be used, as this shares the same lifecycle.
# Drag boundaries
In Pose there was a dragBounds
option that could define the boundaries of the draggable area. It could define top/left/bottom/right boundaries in pixels or a percentage.
In Motion, the dragConstraints
prop can define boundaries only in pixels.
<motion.div
drag="x"
dragConstraints={{ left: 0, right: 100 }}
/>
But now there's also the ability to pass in a ref
to another component, and have that act as the drag constraints for this component. So if that component is styled responsively, the drag constraints will update when the viewport size changes.
const MyComponent = () => {
const constraintsRef = useRef(null)
return (
<>
<div ref={constraintsRef} />
<motion.div drag dragConstraints={constraintsRef} />
</>
)
}
These constraints are enforced elastically now, but we can remove this effect by setting dragElastic
to 0
.
<motion.div
drag
dragConstraints={constraints}
dragElastic={0}
/>
# Passive values
Pose allowed you to passively animate values based on the output of others.
const Box = posed.div({
draggable: "x",
passive: {
y: ["x", latestX => latestX * 2],
},
})
You could attach values either to others on the same component, or those higher up the hierarchy via labels.
This could be useful but the strict hierarchal relationship was limiting and the use of labels created brittle, weak dependencies.
Motion tracks the state and velocity of every animating value with a MotionValue
. Usually, it creates these by default, but by manually creating them they can be passed between components, and interesting relationships can be formed with the useTransform
hook.
import {
motion,
useMotionValue,
useTransform,
} from "framer-motion"
export const Box = () => {
const x = useMotionValue(0)
const y = useTransform(x, latestX => latestX * 2)
return <motion.div drag style={{ x, y }} />
}
# FLIP
In Pose, expensive layout-affecting animations like height
and top
could be automatically converted to performant animations using transforms like scaleY
and y
.
const Menu = posed.div({
open: {
height: "auto",
flip: true,
},
closed: {
height: 0,
flip: true,
},
})
This had a number of problems and limitations. As we re-introduce this feature into Framer Motion we're trying to ensure these don't exist going forward.
With the new layoutTransition
prop, a component will automatically detect when it changes layout as the result of a re-render.
<motion.div layoutTransition />
This layout change can be instigated by any CSS application or DOM change, for instance:
- Being adjacent to a resized component
- Adding/removing a CSS class
- Changing position in the DOM
- Changing children
So not only does the layout transition capability work in a wider variety of use-cases, it also offers more freedom in how you affect the layout. It's also a much clearer name!
# Scaled content
One of the key limitations of the flip
option in Pose was distorted content within components that didn't retain their aspect ratio throughout the layout animation.
This occured because FLIP animations immediately switch to the new layout and then "undo" the layout change using transforms. It then animates to the new layout by animating the transform to none
. So any content within here will look stretched and squashed.
In Motion we can maintain the size and shape of children components with the useInvertedScale
hook. This provides a scaleX
and scaleY
MotionValue
that automatically compensates for the scale of the parent component.
const Text = () => {
const inverted = useInvertedScale()
return <motion.p style={inverted}>No distortion</motion.p>
}
Depending on your layout you'll need to anchor the origin of this scale by choosing originX
and originY
points as the default transform origin of every component is 0.5
. For the effect to work you'll also need to ensure the component is in the same position for both layouts.
# PoseGroup
The final difference between the two libraries is the PoseGroup
component. It provided three main functionalities:
- Unmount animations
- Reorder animations
- FLIP-powered unmount
This FLIP animation to unmount has been removed, as it often lead to unexpected or weird results. We may introduce a similar feature in the future. It also wasn't often needed. However the other two have been split into specific functionalities.
# Unmount animations
There's a new component, AnimatePresence
, that is responsible for unmount animations. It works in much the same way as PoseGroup
, enabling the use of an exit
prop that can either be an object or the name of a variant.
<AnimatePresence>
{isVisible && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
/>
)}
</AnimatePresence>
# Reorder animations
With PoseGroup
, when an item was removed all of the other items would animate smoothly into their new place.
This has been evolved into a new prop called positionTransition
, and it doesn't require the use of AnimatePresence
to work (but they do work nicely together).
<motion.div positionTransition />
It can be set either as a boolean for a default animation, or as a Transition
.
<motion.div
positionTransition={{ type: "spring", stiffness: 500 }}
/>
If defined, when the motion
component changes position on a page as a result of the component re-rendering, it'll use this transition to animate smoothly to its new layout.
Paired with AnimatePresence
, you have all the previous functionality of PoseGroup
.
<AnimatePresence>
{items.map(item => (
<motion.li
key={item.id}
exit={{ opacity: 0 }}
positionTransition
/>
))}
</AnimatePresence>