Everything about Framer Motion layout animations
March 8, 2022 / 17 min read
Last Updated: August 6, 2022Framer Motion has changed a lot since I last wrote about it. So much so that I recently got a bit lost trying to build a specific layout animation and my own blog post that actually looked into this specific type of animation was far from helpful 😅. Despite the updated section I added back in November, it still felt like I was not touching upon several points on this subject and that some of them were incomplete.
On top of the API changes and the many new features that the Framer team added to the package around layout animations, I noticed that there are lots of little tricks that can make your layout animations go from feeling clumsy to absolutely ✨ perfect ✨. However, these are a bit hidden or lack some practical examples to fully understand them.
Thus, I felt it was time to write a dedicated deep dive into all the different types of layout animations. My objective is for this article to be the place you go to whenever you need a refresher on layout animations or get stuck. Additionally, I'll give you some of my own tips and tricks that I use to work around some of the glitches that layout animations can trigger and examples on how to combine them with other tools from the library such as AnimatePresence
to achieve absolutely delightful effects in your projects!
Layout animations fundamentals
Before we dive into the new features and complex examples of layout animations, let's look back at the fundamentals to reacquaint ourselves with how they work.
A brief refresher on layout animations
In Framer Motion, you can animate a motion
component between distinct layouts by setting the layout
prop to true
. This will result in what we call a layout animation.
We can't animate a motion
component between layouts using a combination of initial
and animate
props as we would do for other kinds of Framer Motion animations. For that, we need to use the layout
prop.
In the example below, you'll find a first showcase of a layout animation:
- You can change the position of the
motion
component, the square, along the x axis. - You can enable or disable the
layout
prop for thatmotion
component
1// position: start23<motion.div4style={{5justifySelf: position,6}}7//...8/>9
We can see that each time we change the layout, i.e. a rerender occurs, the layout
prop allows for the component to transition smoothly from its previous layout to the newly selected one. However, without it there is no transition: the square will move abruptly.
Layout animations "smooth things up", and add a certain level of physicality to some user interactions where usually things would transition abruptly. One example where they can shine is when adding/removing elements from a list. I tend to leverage layout animations a lot for use cases like this one, especially combined with other Framer Motion features such as AnimatePresence
.
The playground below showcases one of my own NotificationList
component that leverages layout animations:
- each notification is wrapped in a
motion
component with thelayout
prop set totrue
. - the overall list is wrapped in
AnimatePresence
thus allowing each item in a list to have anexit
animation. - clicking on any of the notifications on the list will remove them and, thanks to layout animations, the stack will gracefully readjust itself.
import { motion, AnimatePresence } from 'framer-motion'; import React from 'react'; import { Wrapper, Toast } from './Components'; import './scene.css'; const ITEMS = ['Welcome 👋', 'An error occurred 💥', 'You did it 🎉!', 'Success ✅', 'Warning ⚠️']; const Notifications = () => { const [notifications, setNotifications] = React.useState(ITEMS) return ( <Wrapper> <AnimatePresence> {notifications.map((item) => <motion.div key={item} onClick={() => setNotifications((prev) => prev.filter(notification => notification !== item))} layout initial={{ y: 150, x: 0, opacity: 0, }} animate={{ y: 0, x: 0, opacity: 1, }} exit={{ opacity: 0, }} > <Toast>{item}</Toast> </motion.div> )} </AnimatePresence> </Wrapper> ); } export default Notifications
Fixing distortions
When performing a layout animation that affects the size of a component, some distortions may appear during the transition for some properties like borderRadius
or boxShadow
. These distortions will occur even if these properties are not part of the animation.
Luckily, there's an easy workaround to fix those: set these properties as inline styles as showcased below:
1// expanded: false23// CSS4.box {5width: 20px;6height: 20px;7border-radius: 20px;8}910.box[data-expanded="true"] {11width: 150px;12height: 150px;13}1415// JS16<motion.div17layout18className="box"19data-expanded={expanded}20/>2122
More about the layout prop
We just saw that setting the layout
prop to true
gives us the ability to animate a component between layouts by transitioning any properties related to its size or position. I recently discovered that there are more values that the layout
prop can take:
layout="position"
: we only smoothly transition the position-related properties. Size-related properties will transition abruptly.layout="size"
: we only smoothly transition the size-related properties. Position-related properties will transition abruptly.
To illustrate this, I built the widget below that showcases how the transition of a motion
component is altered based on the value of the layout
prop:
Why would we need to use these other layout
properties? What's the practical use? you may ask. Sometimes, as a result of a layout animation, the content of a component that resizes can end up "squished" or "stretched". If you see this happening when working on a layout animation, chances are that it can be fixed by simply setting the layout
prop to position
.
Below you'll find an example of such a use case:
- Removing items in this horizontal list will affect the size of each component. By default, you will notice the components getting slightly squished when an item is removed.
- Wrapping the content in a
motion
component and settinglayout
toposition
by toggling the switch will fix all the distortions you may observe on the content of themotion
block. Each component will resize gracefully with a more natural transition.
1<motion.div layout>2<Label variant="success">3<div4style={{5width: '100%',6display: 'flex',7justifyContent: 'start',8}}9>10<DismissButton/>11<span>{text}</span>12</div>13</Label>14</motion.div>
Reorder
Drag-to-reorder items in a list where each item then smoothly moves to its final position is perhaps the best in class use case when it comes to layout animations. It's actually the first use case I thought about when I discovered layout animations in the first place a year ago.
Lucky us, the developers at Framer gave us a ready-to-use set of components to handle that specific use case with ease 🎉. They provided 2 components that we're going to use in follow-up examples:
Reorder.Group
where we pass our list of items, the direction of the reorder (horizontal or vertical), and theonReorder
callback which will return the latest order of the listReorder.Item
where we pass the value of an item in the list
Simple examples of drag-to-reorder list using Reorder
1const MyList = () => {2const [items, setItems] = React.useState(['Item 1', 'Item 2', 'Item 3']);34return (5<Reorder.Group6// Specify the direction of the list (x for horizontal, y for vertical)7axis="y"8// Specify the full set of items within your reorder group9values={items}10// Callback that passes the newly reordered list of item11// Note: simply passing a useState setter here is equivalent to12// doing `(reordereditems) => setItmes(reordereditems)`13onReorder={setItems}14>15{items.map((item) => (16// /!\ don't forget the value prop!17<Reorder.Item key={item} value={item}>18{item}19</Reorder.Item>20))}21</Reorder.Group>22);23};
With just a few lines of code, we can get a ready-to-use list with a drag-to-reorder effect! And that's not all of it:
- Each
Reorder.Item
is a motion component - Each
Reorder.Item
component in the list is able, out-of-the-box, to perform layout animations
Thus it's very easy to add a lot more animations on top of this component to build a truly delightful user experience. There are, however, two little catches that I only discovered when I started working with the Reorder
components 👇
Combining everything
In the playground below, you will find a more advanced example that leverages Reorder.Group
and Reorder.Item
along with some other aspects of layout animations that we saw earlier:
- Finish blog post ✍️
- Build new Three.js experiences ✨
- Add new components to Design System 🌈
- Make some coffee ☕️
- Drink water 💧
- Go to the gym 🏃♂️
layout="position"
is used on the content of each item to avoid distortions when they are selected and a layout animation is performed- Custom React styled-components use
Reorder
components through polymorphism
1//...23<Card4as={Reorder.Item}5//...6value={item}7>8<Card.Body as={motion.div} layout="position">9<Checkbox10id={`checkbox-${item.id}`}11aria-label="Mark as done"12checked={item.checked}13onChange={() => completeItem(item.id)}14/>15<Text>{item.text}</Text>16</Card.Body>17</Card>1819//...
- Inline styles are used for the
borderRadius
of the item to avoid distortions when the item resizes position: relative
has been added as inline style to theReorder.Item
to fix overlap issues that occur while dragging elements of the list over one anotherAnimatePresence
is used to allow for exit animations when elements are removed from the list
1//...2<AnimatePresence>3{items.map((item) => (4<motion.div5exit={{ opacity: 0, transition: { duration: 0.2 } }}6/>7<Card8as={Reorder.Item}9style={{10position: 'relative', // this is needed to avoid weird overlap11borderRadius: '12px', // this is set as inline styles to avoid distortions12width: item.checked ? '70%' : '100%', // will be animated through layout animation13}}14value={item}15>16//...17</Card>18</motion.div>19//...20)}21</AnimatePresence>22//...
- The list and its sibling elements are wrapped in a
LayoutGroup
to perform smooth layout animations when the task list updates and changes the overall layout
1<LayoutGroup>2<Reorder.Group axis="y" values={items} onReorder={setItems}>3<AnimatePresence>4{//...}5</AnimatePresence>6</Reorder.Group>7<motion.div layout>8<hr />9<span>Check items off the list when you're done!</span>10</motion.div>11</LayoutGroup>
Want to run this example yourself and hack on top of it? You can find the full implementation of this example on my blog's Github repository.
Conclusion
You now know pretty much everything there is to know about Framer Motion layout animations 🎉. Whether it's for some basic use cases, such as the Notification List we've seen in the first part, adding little details like the shared layout animations from the tabs components, to building reorderable lists with complex transitions: layout animations have no more secrets to you.
I hope this blog post can serve you as a guide/helper to make your own animations look absolutely perfect ✨, especially when working on the nitty-gritty details of your transitions. It may sound overkill to spend so much time reading and working around the issues we showcased in this blog post, but trust me, it's worth it!
Want to go further?
I'd suggest taking a look at some of the complex examples provided in the Framer Motion documentation. The team came up with very good examples such as this drag to reorder tabs component which contains every concept used in the task list example that I introduced in this blog post. After that, I'd try to see where you could sprinkle some layout animation magic on your own projects 🪄. There's no better way of learning than building things by yourself!
Liked this article? Share it with a friend on Twitter or support me to take on more ambitious projects to write about. Have a question, feedback or simply wish to contact me privately? Shoot me a DM and I'll do my best to get back to you.
Have a wonderful day.
– Maxime
A complete guide to Framer Motion layout animations showcasing every concept, from the layout prop, shared layout animations and LayoutGroup, to complex drag-to-reorder interactions.