

Everything about Framer Motion layout animations
source link: https://blog.maximeheckel.com/posts/framer-motion-layout-animations/?ref=sidebar
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Everything about Framer Motion layout animations
March 8, 2022 / 17 min read /
318 Likes โข
13 Replies โข
71 Mentions
Framer 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!
Looking for an intro to Framer Motion?
Don't worry I got your back! You can check out my guide to creating animations that spark joy with Framer Motion to get started.
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.
When we're talking about animating the "layout" or a "layout property" we mean updating any of the following properties:
- ArrowAn icon representing an arrowPosition-related, such as CSS
flex
,position
orgrid
- ArrowAn icon representing an arrowSize-related, such as CSS
width
orheight
- ArrowAn icon representing an arrowThe overall position of an element within a list for example. This can useful if you want to animate sorting/reordering a list.
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:
- ArrowAn icon representing an arrowYou can change the position of the
motion
component, the square, along the y axis. - ArrowAn icon representing an arrowYou can enable or disable the
layout
prop for thatmotion
component
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:
- ArrowAn icon representing an arroweach notification is wrapped in a
motion
component with thelayout
prop set totrue
. - ArrowAn icon representing an arrowthe overall list is wrapped in
AnimatePresence
thus allowing each item in a list to have anexit
animation. - ArrowAn icon representing an arrowclicking 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'; 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
You can customize the transition of your layout animations by setting it up within a layout
key in your transition object:
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:
If like me, you're using CSS variables in your codebase, just be warned that setting a CSS variable for the value of borderRadius
or boxShadow
will not fix any of the side effects showcased above.
You will need to use a proper value to avoid any distortions.
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:
- ArrowAn icon representing an arrow
layout="position"
: we only smoothly transition the position-related properties. Size-related properties will transition abruptly. - ArrowAn icon representing an arrow
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:
- ArrowAn icon representing an arrowRemoving 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.
- ArrowAn icon representing an arrowWrapping 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.
Shared layout animations and LayoutGroup
These two concepts are perhaps what I struggled the most with recently as:
- ArrowAn icon representing an arrowthey appear to be closely related based on their names but have very distinct purposes and use cases
- ArrowAn icon representing an arrowthere has been a lot of API changes in this area. Thus, everything I thought I had mastered was actually brand new and a bit different ๐
And I know I'm not the only one, I've seen many people confusing shared layout animations and LayoutGroup
The confusion is understanding. There used to be a feature called AnimatedSharedLayout
that was necessary to achieve shared layout animations that was deprecated around the same time as LayoutGroup
was introduced.
I first thought LayoutGroup
was meant to replace AnimatedSharedLayout
, but we're going to see in this part that this not really the case.
Shared layout animations
One might think that this is yet another type of layout animation like we saw in the previous part, but with a twist. It's not wrong, but also not quite exact either.
Shared layout animations have their own API, not directly related to the layout
prop. Instead of animating a component's position and size, we are animating a component between all its instances that have a common layoutId
prop. To illustrate this concept let's look at the playground below:
import { motion } from 'framer-motion'; import React from 'react'; import { List, Item, ArrowIcon } from './Components'; const ITEMS = [1, 2, 3]; const SelectableList = () => { const [selected, setSelected] = React.useState(1); return ( <List> {ITEMS.map(item => ( <Item onClick={() => setSelected(item)} onKeyDown={(event: { key: string }) => event.key === 'Enter' ? setSelected(item) : null} tabIndex={0} > <div>Item {item}</div> {item === selected ? <motion.div layoutId="arrow"> <ArrowIcon style={{ height: '24px', color: '#5686F5', transform: 'rotate(-90deg)', }} /> </motion.div> : null } </Item> ))} </List> ) } export default SelectableList
We can see in this example that:
- ArrowAn icon representing an arrowWe're transitioning between multiple instances of the
Arrow
component - ArrowAn icon representing an arrowThey all share a common
layoutId
which tells Framer Motion that these components are related and need to transition from one instance to the newly "active" one when the user clicks on a new item.
The shared aspect comes from the effect of the component moving from one position to another as if it was the same. And that's what I love about shared layout animations. It's all smoke and mirrors. Like a magic trick ๐ช!
The "magic" behind it is actually quite simple:
- ArrowAn icon representing an arrowIn our example above, when clicking on a new element, the
Arrow
component that was displayed on the screen fades away to reveal a new instance of theArrow
component - ArrowAn icon representing an arrowThat new
Arrow
component is the one that will be eventually positioned under our newly selected element on the list - ArrowAn icon representing an arrowThat component then transitions to its final position
To show you this effect, I reused the demo above and gave a different color to each instance of Arrow
so you can better visualize what's happening:
One component I like to decorate with shared layout animations is Tabs
. We can leverage this type of animation to add proper transitions for the "selected indicator" but also to a "hover highlight" like Vercel does on their own Tabs
component! Below is an example implementation of such component with these two layout animations:
- ArrowAn icon representing an arrowWe can see the "selected indicator" transitioning from one tab to another when a new one is selected
- ArrowAn icon representing an arrowThe "hover highlight" will follow the user's mouse when hovering over the
Tabs
component - ArrowAn icon representing an arrowEach shared layout animation has a distinct
layoutId
prop:underline
andhighlight
import { motion } from 'framer-motion'; import React from 'react'; import { Wrapper, Tab } from './Components'; const Tabs = () => { const [focused, setFocused] = React.useState(null); const [selected, setSelected] = React.useState('Item 1'); const tabs = ['Item 1', 'Item 2', 'Item 3']; return ( <Wrapper onMouseLeave={() => setFocused(null)}> {tabs.map((item) => ( <Tab key={item} onClick={() => setSelected(item)} onKeyDown={(event: { key: string }) => event.key === 'Enter' ? setSelected(item) : null } onFocus={() => setFocused(item)} onMouseEnter={() => setFocused(item)} tabIndex={0} > <span>{item}</span> {focused === item ? ( <motion.div transition={{ layout: { duration: 0.2, ease: 'easeOut', }, }} style={{ position: 'absolute', bottom: '-2px', left: '-10px', right: 0, width: '140%', height: '110%', background: '#23272F', borderRadius: '8px', zIndex: 0, }} layoutId="highlight" /> ) : null} {selected === item ? ( <motion.div style={{ position: 'absolute', bottom: '-10px', left: '0px', right: 0, height: '4px', background: '#5686F5', borderRadius: '8px', zIndex: 0, }} layoutId="underline" /> ) : null} </Tab> ))} </Wrapper> ); } export default Tabs;
There's however one little problem. What if we wanted to build a reusable component that has a shared layout animation defined and use it twice within the same page? Well, both seemingly distinct shared layout animation would end up with the same layoutId
prop which, as a result, would cause things to get a bit weird:
- Item 1
- Item 2
- Item 3
- Item 1
- Item 2
- Item 3
This is where LayoutGroup
comes into the picture ๐.
LayoutGroup: the namespacing use case
For this use case, we can see LayoutGroup
as a tool to use on top of shared layout animations and not directly related to them as it may have first seemed.
We saw above that layoutId
props do not take into consideration which instance of a component they are used in, i.e. they are global. In this first use case, we'll use it to namespace our shared layout animations: give them a unique id
so they can be rendered multiple times and still behave distinctly.
Namespacing multiple instance of shared layout animations with LayoutGroup
By using LayoutGroup
in our Tabs
component implementation, we can now make it a truly reusable component and work around the bug we showcased in the previous part: the shared layout animations are now only "shared" within their own LayoutGroup
.
- Item 1
- Item 2
- Item 3
- Item 1
- Item 2
- Item 3
LayoutGroup: the grouping use case
Namespacing shared layout animations is not the only use case for LayoutGroup
. Its original purpose is actually to:
Group motion components that should perform layout animations together.
But what does that exactly mean?
We saw in the first part that a layout animation will transition a component from one layout to another when a rerender occurs. That works fantastically well for everything within the motion
component with the layout
prop, but what about the sibling components?
As a result of one component's layout animation, the overall layout of the page may be affected. For example when removing an item from a list, all the surrounding components will need to adapt through a transition or a resize. The problem here is that there's no way to make those other components transition smoothly as is because:
- ArrowAn icon representing an arrowthey are not necessarily
motion
components themselves - ArrowAn icon representing an arrowthey are not rerendering since not interacted with
- ArrowAn icon representing an arrowsince they are not rerendering they are unable to perform a layout animation by themselves, even if defined.
This can be fixed by wrapping each sibling components in a motion
component with the layout
set to true
(if the siblings were not motion
components themselves already), and wrapping all the components we wish to perform a smooth transition when the overall layout changes in a LayoutGroup
.
In the little widget below I showcase this by rendering two instances of a list component where each item is a motion
component:
- ArrowAn icon representing an arrowTry to remove an item from the first list and notice that the items within the first list perform a smooth layout animation and that the second list, however, moves abruptly
- ArrowAn icon representing an arrowToggle
LayoutGroup
wrapping on and notice that now when removing an item from the first list, the second list transition smoothly to its target position.
To conclude this part, LayoutGroup
has two use cases:
- ArrowAn icon representing an arrowNamespacing
layoutId
which allows us to build reusable components that leverage shared layout animation and use those components within the same page - ArrowAn icon representing an arrowGrouping together sibling components that perform distinct layout animations that may impact the overall layout on the page so they can adapt gracefully to the new updated layout.
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:
- ArrowAn icon representing an arrow
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 list - ArrowAn icon representing an arrow
Reorder.Item
where we pass the value of an item in the list
Simple examples of drag-to-reorder list using Reorder
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:
- ArrowAn icon representing an arrowEach
Reorder.Item
is a motion component - ArrowAn icon representing an arrowEach
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 ๐
When I tried the basic example the first time I noticed a very odd effect:
You can see that there's a strange overlap issue happening: the item being dragged sometimes renders behind its siblings. It would feel more natural to have the element being dragged always on top of its siblings right?
It doesn't happen consistently, but if you see this don't worry. There's a simple workaround for this issue: setting the position
CSS property to relative
for each instance of Reorder.Item
.
Both Reorder.Group
and Reorder.Item
support polymorphism, i.e. they let the developer pick the underlying HTML tag that will be rendered. However, unlike other library that support polymorphism, here you can only pass HTML elements.
This prop will not accept custom React components as of writing this blog post. There's, luckily, an easy way around this. If your component library/design system supports polymorphism, you can work around this limitation by simply passing the desired Reorder
component in your component's as
prop:
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:
Check items off the list when you're done!
- ArrowAn icon representing an arrow
layout="position"
is used on the content of each item to avoid distortions when they are selected and a layout animation is performed - ArrowAn icon representing an arrowCustom React styled-components use
Reorder
components through polymorphism
- ArrowAn icon representing an arrowInline styles are used for the
borderRadius
of the item to avoid distortions when the item resizes - ArrowAn icon representing an arrow
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 another - ArrowAn icon representing an arrow
AnimatePresence
is used to allow for exit animations when elements are removed from the list
- ArrowAn icon representing an arrowThe 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
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!
Already 347 awesome people liked, shared or talked about this article:
Tweet about this post and it will show up here! Or, click here to leave a comment and discuss about it on Twitter.
Do you have any questions, comments or simply wish to contact me privately? Donโt hesitate to shoot me a DM on Twitter.
Have a wonderful day.
Maxime
Subscribe to my newsletter
Get email from me about my ideas, frontend development resources and tips as well as exclusive previews of upcoming articles.
Recommend
-
46
Frame Motion is an open source React library to power production-ready animations. Indrek Lasn ...
-
71
From basics to complex orchestrated animations Konstanti...
-
48
Framer-motion is a library that powers animations in
-
18
React hero animations โญ๏ธ If you like Motion Layout, give it a star on GitHub! โญ๏ธMotion LayoutC...
-
15
-
52
-
7
Introduction to Framer Motion for ReactHow to create animations with React
-
128
Responses
-
14
@parmeetsasijaParmeet Singh AsijaFreelance Frontend Developer with 2+ years experience in React, Javascript and Typescript | Software Engineer
-
13
Framer Motion examples for React animationsSeptember 1, 2022 ยท 10 min readJoel EzimorahSoftware Developer
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK