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.
// Poseconst 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.
// Motionconst 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).
// Motionconst 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.
// Poseconst duration = 1000
// Motionconst duration = 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 }} />}