惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

U
Unit 42
P
Proofpoint News Feed
The Last Watchdog
The Last Watchdog
S
Secure Thoughts
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
N
News | PayPal Newsroom
Application and Cybersecurity Blog
Application and Cybersecurity Blog
O
OpenAI News
S
Security @ Cisco Blogs
宝玉的分享
宝玉的分享
Hacker News: Ask HN
Hacker News: Ask HN
T
Troy Hunt's Blog
Google Online Security Blog
Google Online Security Blog
WordPress大学
WordPress大学
有赞技术团队
有赞技术团队
TaoSecurity Blog
TaoSecurity Blog
Help Net Security
Help Net Security
Latest news
Latest news
NISL@THU
NISL@THU
S
Security Affairs
博客园_首页
C
CXSECURITY Database RSS Feed - CXSecurity.com
博客园 - 聂微东
AI
AI
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Recent Announcements
Recent Announcements
P
Privacy & Cybersecurity Law Blog
小众软件
小众软件
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Hugging Face - Blog
Hugging Face - Blog
博客园 - 司徒正美
AWS News Blog
AWS News Blog
W
WeLiveSecurity
Google DeepMind News
Google DeepMind News
I
InfoQ
Schneier on Security
Schneier on Security
Recent Commits to openclaw:main
Recent Commits to openclaw:main
T
The Exploit Database - CXSecurity.com
IT之家
IT之家
T
Threatpost
Scott Helme
Scott Helme
L
LINUX DO - 热门话题
腾讯CDC
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
N
News and Events Feed by Topic
L
LINUX DO - 最新话题
F
Full Disclosure
大猫的无限游戏
大猫的无限游戏
H
Heimdal Security Blog
S
SegmentFault 最新的问题

Maxime Heckel's Blog

On Rendering the Sky, Sunsets, and Planets - The Blog of Maxime Heckel Shades of Halftone - The Blog of Maxime Heckel Field Guide to TSL and WebGPU - The Blog of Maxime Heckel On Shaping Light: Real-Time Volumetric Lighting with Post-Processing and Raymarching for the Web - The Blog of Maxime Heckel Speaking at Figma Config 2025 - The Blog of Maxime Heckel Post-Processing Shaders as a Creative Medium - The Blog of Maxime Heckel On Crafting Painterly Shaders - The Blog of Maxime Heckel The Art of Dithering and Retro Shading for the Web - The Blog of Maxime Heckel Moebius-style post-processing and other stylized shaders - The Blog of Maxime Heckel Shining a light on Caustics with Shaders and React Three Fiber - The Blog of Maxime Heckel Real-time dreamy Cloudscapes with Volumetric Raymarching - The Blog of Maxime Heckel Painting with Math: A Gentle Study of Raymarching - The Blog of Maxime Heckel Building a magical AI-powered semantic search from scratch - The Blog of Maxime Heckel Beautiful and mind-bending effects with WebGL Render Targets - The Blog of Maxime Heckel Refraction, dispersion, and other shader light effects - The Blog of Maxime Heckel The magical world of Particles with React Three Fiber and Shaders - The Blog of Maxime Heckel The Study of Shaders with React Three Fiber - The Blog of Maxime Heckel Building a Design System from scratch - The Blog of Maxime Heckel Everything about Framer Motion layout animations - The Blog of Maxime Heckel Building a Vaporwave scene with Three.js - The Blog of Maxime Heckel Cubic Bézier: from math to motion - The Blog of Maxime Heckel First steps with GPT-3 for frontend developers - The Blog of Maxime Heckel Building the perfect GitHub CI workflow for your frontend team - The Blog of Maxime Heckel Migrating to Next.js - The Blog of Maxime Heckel Static Tweets with MDX and Next.js - The Blog of Maxime Heckel Scrollspy demystified - The Blog of Maxime Heckel The Power of Composition with CSS Variables - The Blog of Maxime Heckel My first failed SwiftUI project - The Blog of Maxime Heckel Guide to creating animations that spark joy with Framer Motion - The Blog of Maxime Heckel Using Shortcuts and serverless to build a personal Apple Health API - The Blog of Maxime Heckel SEO mistakes I've made and how I fixed them - The Blog of Maxime Heckel Going native: SwiftUI from the perspective of a React developer - The Blog of Maxime Heckel Build your own preview deployment service - The Blog of Maxime Heckel The little guide to CI/CD for frontend developers - The Blog of Maxime Heckel Immigrating to the US - The Blog of Maxime Heckel The physics behind spring animations - The Blog of Maxime Heckel Generate screenshots of your code with a serverless function - The Blog of Maxime Heckel How to use Framer Motion with Emotion styled-components - The Blog of Maxime Heckel Data Fetching with NextJS: What I learned - The Blog of Maxime Heckel Learning in public - The Blog of Maxime Heckel Fixing the dark mode flash issue on server rendered websites - The Blog of Maxime Heckel How to fix NPM link duplicate dependencies issues - The Blog of Maxime Heckel Running scheduled cross-browser end-to-end tests on Github CI - The Blog of Maxime Heckel How I built my first custom ESLint rule - The Blog of Maxime Heckel React Lazy: a take on preloading views - The Blog of Maxime Heckel Automated UI accessibility testing with Cypress - The Blog of Maxime Heckel Building a GraphQL wrapper for the Docker API - The Blog of Maxime Heckel Switching off the lights - Adding dark mode to your React app - The Blog of Maxime Heckel Getting started with Typescript on Gatsby - The Blog of Maxime Heckel Rebuilding Redux with Hooks and Context - The Blog of Maxime Heckel Asynchronous rendering with React - The Blog of Maxime Heckel Using Flow generics to type generic React components - The Blog of Maxime Heckel How to efficiently type your styled-components with Flow - The Blog of Maxime Heckel How I got started with Kubernetes on GKE - The Blog of Maxime Heckel React sub-components Part 3: Whitelisting sub-components with flow - The Blog of Maxime Heckel React sub-components Part 2: Using the new Context API - The Blog of Maxime Heckel React sub-components - The Blog of Maxime Heckel Running Golang tests with Jest - The Blog of Maxime Heckel No title No title
Advanced animation patterns with Framer Motion - The Blog of Maxime Heckel
Maxime Heckel · 2021-04-20 · via Maxime Heckel's Blog

I got ✨a lot✨ of positive feedback from my Guide to creating animations that spark joy with Framer Motion, and it's undeniable that this library has piqued many developers' interests in the world of web-based animations.

While I introduced in this previous post many of the foundational pieces that compose an animation, and how one can orchestrate multiple transitions very easily with Framer Motion, I did not touch upon many of the more advanced features that this library provides.

Ever wondered how to propagate animations throughout several components or to orchestrate complex layout transitions? Well, this article will tell you all about these advanced patterns and show you some of the great things one can accomplish with Framer Motion!

Propagation

One of the first advanced patterns I got to encounter when I tried to add some micro-interactions with Framer Motion on my projects is propagation. I quickly learned that it's possible to propagate changes of variants from a parent motion component to any child motion component. However, this got me confused at the beginning because it broke some of the mental models I originally had when it comes to defining animations.

Remember in my previous blog post when we learned that every Framer Motion Animation needed 3 properties (props) initial, animate, transition, to define a transition/animation? Well, for this pattern that's not entirely true.

Framer Motion allows variants to "flow down" through every motion child component as long as these motion components do not have an animate prop defined. Only the parent motion component, in this case, defines the animate prop. The children themselves only define the behavior they intent to have for those variants.

A great example where I used propagation on this blog is the "Featured" section on the home page of this blog. When you hover it, the individual cards "glow" and this effect is made possible by this pattern. To explain what really is happening under the hood, I built this little widget below where I reproduced this effect:

Hover me!

You can see that hovering (or tapping if you're on mobile) the card or even the label above it triggers the glow effect. What kind of sorcery is this?! By clicking on the "perspective" button, you can see what happens under the hood:

  1. There's an "invisible" motion layer covering the card and the label. This layer holds the whileHover prop which sets the variant "hover"

  2. The "glow" itself is a motion component as well, however, the only thing it defines is its own variants object with a hover key.

Thus when hovering this invisible layer, we toggle the "hover" variant and any child motion component having this variant define in their variants prop will detect this change and toggle the corresponding behavior.

Example of propagation pattern with Framer Motion

1

const CardWithGlow = () => {

13

<motion.div initial="initial" whileHover="hover">

15

<motion.div variants={glowVariants} className="glow"/>

17

<div>Some text on the card/div>

Now let's apply what we learned about the propagation mechanism of Framer Motion! In the playground below you'll find a motion component with a "hover" animation. When hovering it, a little icon will show up on the right end side of that component. You can try to:

  • Modify the variant key used in the motion component wrapping the button and see that now that it defers from what's being set by the parent component, the animation does not trigger and the button is not visible on hover.

  • Set ananimate prop on the motion component that wraps the button and see that it now animates on its own and does not consume the variant set by the parent on hover.

import { styled } from '@stitches/react';
import { motion } from 'framer-motion';
import './scene.css';

const ListItem = styled(motion.li, {
  width: '100%',
  minWidth: '300px',
  background: 'hsla(222, 89%, 65%, 10%)',
  boxShadow: '0 0px 10px -6px rgba(0, 24, 40, 0.3)',
  borderRadius: '8px',
  padding: '8px',
  display: 'flex',
  alignItems: 'center',
  justifyContent: 'space-between',
  cursor: 'pointer',
  marginBottom: '0px',
  color: 'hsl(223, 15%, 65%)',
  fontSize: 18,
});

const Button = styled('button', {
  background: 'transparent',
  cursor: 'pointer',
  border: 'none',
  shadow: 'none',
  color: 'hsl(223, 15%, 65%)',
  display: 'flex',
});

const InfoBox = styled('div', {
  width: '50%',
});

const ARTICLES = [
  {
    category: 'swift',
    title: 'Intro to SwiftUI',
    description: 'An article with some SwitftUI basics',
    id: 1,
  },
];

const Item = (props) => {
  const { article } = props;

  const readButtonVariants = {
    hover: {
      opacity: 1,
    },
    
    
    initial: {
      opacity: 0,
    },
    magic: {
      rotate: 360,
      opacity: 1,
    },
  };

  return (
    <ListItem layout initial="initial" whileHover="hover">
      <InfoBox>{article.title}</InfoBox>
      <motion.div
        
        
        variants={readButtonVariants}
        transition={{ duration: 0.25 }}
      >
        <Button
          aria-label="read article"
          title="Read article"
          onClick={(e) => e.preventDefault()}
        >
          &#8594;
        </Button>
      </motion.div>
    </ListItem>
  );
};

const Example = () => <Item article={ARTICLES[0]} />;

export default Example;

Animate components when they are unmounting

So far, we've only seen examples of animation being triggered either on mount or following some specific events like hover or tap. But what about triggering an animation right before a component unmounts? Some sort of "exit" transition?

Well, in this second part, we'll take a look at the Framer Motion feature that addresses this use case and also the one that impressed me the most: AnimatePresence!

I tried to implement some kind of exit animations before learning about AnimatePresence, but it was hacky and always required extra code to set a proper "transitional" state (like isClosing, isOpening) and toggle the corresponding animation of that state. As you can imagine, it was very error-prone.

A very hacky way to implement an exist animation without AnimatePresence

6

const MagicComponent = () => {

7

const [hidden, setHidden] = React.useState(false);

8

const [hidding, setHidding] = React.useState(false);

11

animate: (hidding) => ({

12

opacity: hidding ? 0 : 1,

19

const hideButton = () => {

21

setTimeout(() => setHidden(true), 1500);

On the other hand, AnimatePresence is extremely well thought of and easy to use. By simply wrapping any motion component in an AnimatePresence component, you'll have the ability to set an exit prop!

Example of use case for AnimatePresence

1

const MagicComponent = () => {

2

const [hidden, setHidden] = React.useState(false);

8

initial={{ opacity: 1 }}

10

onClick={() => setHidden(true)}

In the interactive widget below, I showcase 2 versions of the same component:

  • the one on the left is not wrapped in AnimatePresence

  • the second one, however, is wrapped

That's the only difference code-wise. But as you can see the difference is pretty striking!

We now have a new awesome tool to use to make our transitions even better! It's time it a try in the playground below:

  • Try to remove the AnimatePresence component. Notice how this makes Framer Motion skip the animation specified in the exit prop.

  • Try to modify the animation defined in the exit prop. For example, you could make the whole component scale from 1 to 0 while it exit. (I already added the proper animation objects commented in the code below 😄)

import { styled } from '@stitches/react';
import { AnimatePresence, motion } from 'framer-motion';
import React from 'react';
import Pill from './Pill';
import './scene.css';

const List = styled(motion.ul, {
  padding: '16px',
  width: '350px',
  background: ' hsl(223, 15%, 10%)',
  borderRadius: '8px',
  display: 'grid',
  gap: '16px',
});


const ListItem = styled(motion.li, {
  minWidth: '300px',
  background: 'hsla(222, 89%, 65%, 10%)',
  boxShadow: '0 0px 10px -6px rgba(0, 24, 40, 0.3)',
  borderRadius: '8px',
  padding: '8px',
  display: 'flex',
  alignItems: 'center',
  justifyContent: 'space-between',
  cursor: 'pointer',
  marginBottom: '0px',
  color: 'hsl(223, 15%, 65%)',
  fontSize: 18,
});

const Button = styled('button', {
  background: 'transparent',
  cursor: 'pointer',
  border: 'none',
  shadow: 'none',
  color: 'hsl(223, 15%, 65%)',
  display: 'flex',
});

const InfoBox = styled('div', {
  width: '50%',
});

const FilterWrapper = styled('div', {
  marginBottom: '16px',
  input: {
    marginRight: '4px',
  },
  label: {
    marginRight: '4px',
  },
});

const ARTICLES = [
  {
    category: 'swift',
    title: 'Intro to SwiftUI',
    description: 'An article with some SwitftUI basics',
    id: 1,
  },
  {
    category: 'js',
    title: 'Awesome React stuff',
    description: 'My best React tips!',
    id: 2,
  },
  {
    category: 'js',
    title: 'Styled components magic',
    description: 'Get to know ways to use styled components',
    id: 3,
  },
  {
    category: 'ts',
    title: 'A guide to Typescript',
    description: 'Type your React components!',
    id: 4,
  },
];

const categoryToVariant = {
  js: 'warning',
  ts: 'info',
  swift: 'danger',
};

const Item = (props) => {
  const { article, showCategory } = props;

  const readButtonVariants = {
    hover: {
      opacity: 1,
    },
    initial: {
      opacity: 0,
    },
  };

  return (
    <ListItem initial="initial" whileHover="hover">
      <InfoBox>{article.title}</InfoBox>
      {}
      <AnimatePresence>
        {showCategory && (
          <motion.div
            initial={{ opacity: 0 }}
            
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            
          >
            <Pill variant={categoryToVariant[article.category]}>
              {article.category}
            </Pill>
          </motion.div>
        )}
      </AnimatePresence>
      <motion.div variants={readButtonVariants} transition={{ duration: 0.25 }}>
        <Button
          aria-label="read article"
          title="Read article"
          onClick={(e) => e.preventDefault()}
        >
          &#8594;
        </Button>
      </motion.div>
    </ListItem>
  );
};

const Component = () => {
  const [showCategory, setShowCategory] = React.useState(false);

  return (
    <>
      <FilterWrapper>
        <div>
          <input
            type="checkbox"
            id="showCategory"
            checked={showCategory}
            onChange={() => setShowCategory((prev) => !prev)}
          />
          <label htmlFor="showCategory">Show Category</label>
        </div>
      </FilterWrapper>
      <List>
        {ARTICLES.map((article) => (
          <Item
            key={article.id}
            article={article}
            showCategory={showCategory}
          />
        ))}
      </List>
    </>
  );
};

export default Component;

Layout animations

We now know how to:

  • propagate animations throughout a set of motion components

  • add an exit transition to a component so it can unmount gracefully

Those advanced patterns should give us the ability to craft some pretty slick transitions right? Well, wait until you hear more about how Framer Motion can handle layout animations!

What is a "layout animation"?

A layout animation is any animation touching layout related properties such as:

  • position properties

  • flex or grid properties

  • width or height

  • sorting elements

But to give you a little bit more of an idea of what I'm talking about here, let's try to take a look at the playground below that showcases 2 versions of the same component:

  • the first one animates justify-content property between flex-start and flex-end by simply using the patterns we only know so far: setting this property in the animation prop

  • the second one uses a new prop: layout. It's here set to true to tell Framer Motion that a "layout related property", and thus by extension the layout of the component, will change between rerenders. The properties themselves are simply defined in CSS as any developer would do normally when not using Framer Motion.

import { styled } from '@stitches/react';
import { AnimatePresence, motion } from 'framer-motion';
import React from 'react';
import './scene.css';

const SwitchWrapper1 = styled(motion.div, {
  width: '50px',
  height: '30px',
  borderRadius: '20px',
  cursor: 'pointer',
  display: 'flex',
});

const SwitchHandle1 = styled(motion.div, {
  background: '#fff',
  width: '30px',
  height: '30px',
  borderRadius: '50%',
});


const Switch1 = () => {
  const [active, setActive] = React.useState(false);

  const switchVariants = {
    initial: {
      backgroundColor: '#111',
    },
    animate: (active) => ({
      backgroundColor: active ? '#f90566' : '#111',
      justifyContent: active ? 'flex-end' : 'flex-start',
    }),
  };

  return (
    <SwitchWrapper1
      initial="initial"
      animate="animate"
      onClick={() => setActive((prev) => !prev)}
      variants={switchVariants}
      custom={active}
    >
      <SwitchHandle1 />
    </SwitchWrapper1>
  );
};

const SwitchWrapper2 = styled('div', {
  width: '50px',
  height: '30px',
  borderRadius: '20px',
  cursor: 'pointer',
  display: 'flex',
  background: '#111',
  justifyContent: 'flex-start',

  '&[data-isactive="true"]': {
    background: '#f90566',
    justifyContent: 'flex-end',
  },
});

const SwitchHandle2 = styled(motion.div, {
  background: '#fff',
  width: '30px',
  height: '30px',
  borderRadius: '50%',
});


const Switch2 = () => {
  const [active, setActive] = React.useState(false);

  return (
    <SwitchWrapper2
      data-isactive={active}
      onClick={() => setActive((prev) => !prev)}
    >
      <SwitchHandle2 layout />
    </SwitchWrapper2>
  );
};

const Example = () => (
  <div style={{ maxWidth: '300px' }}>
    <p>
      Switch 1: Attempt at animating justify-content in a Framer Motion animation
      object.
    </p>
    <Switch1 />
    <br />
    <p>
      Switch 2: Animating justify-content using layout animation and the layout prop.
    </p>
    <Switch2 />
  </div>
);

export default Example;

We can observe multiple things here:

  1. The first example does not work, it looks here that Framer Motion can't transition between justify-content properties the same way you'd transition an opacity from 0 to 1 gracefully.

  2. The second component however transitions as expected between the flex-start and flex-end property. By setting layout to true in the motion component, Framer Motion can transition the component's justify-content property smoothly.

  3. Another advantage of the second component: it does not have as much of a "hard dependency" with Framer Motion as the first one. We could simply replace the motion.div with a simple div and the component itself would still work

Shared Layout Animation

We now know what layout animations are and how to leverage those for some specific use cases. But what happens if we start having layout animations that span several components?

In the more recent versions of Framer Motion, building shared layout animations has been greatly improved: the only thing we need to do is set a common layoutId prop to the components that are part of a shared layout animation.

Below, you'll find a widget that showcases an example of shared layout animation.

  • 🐶

  • 🐱

  • 🐰

  • 🐭

  • 🐹

  • 🐷

  • 🐻

  • 🦁

  • 🦊

  • 🐧

  • 🐼

  • 🐮

When clicking on one of the emojis in the example above you will notice that:

  • the border will gracefully move to the newly selected element when the common layoutId is enabled

  • the border will abruptly appear around the newly selected element when the common layoutId is disabled (i.e. not defined or different)

All we need to do to obtain this seemingly complex animation was to add a prop, that's it! ✨ In this example in particular, all I added is a common layoutId called border to every instance of the blue circle component.

Example of shared animate layout using the "layoutId" prop

1

const MagicWidgetComponent = () => {

2

const [selectedID, setSelectedID] = React.useState('1');

12

onClick={() => setSelectedID(item.id)}

14

<Circle>{item.photo}</Circle>

15

{selectedID === item.id && (

23

border: '4px solid blue';

It's now time to give a try at what we just learned! This last example compiles all the previous playgrounds together to create this list component. This implementation includes:

  • using the layout prop on the ListItem component to animate reordering the list

  • using the layout prop on the list itself to handle resizing gracefully when items are expanded when clicked on

  • other instances of the layout prop used to prevent glitches during a layout animation (especially the ones involving changing the height of a list item)

You can try to:

  • comment out or remove the layout prop on the ListItem and see that now, reordering happens abruptly 👉 no more transition!

  • comment out or remove the LayoutGroup and notice how this affects all the layout animations

  • try to add the layout prop on the <Title/> component and see it gracefully adjusting when the height of an item changes

import { styled } from '@stitches/react';
import { AnimatePresence, LayoutGroup, motion } from 'framer-motion';
import React from 'react';
import Pill from './Pill';
import './scene.css';

const List = styled(motion.ul, {
  padding: '16px',
  width: '350px',
  background: ' hsl(223, 15%, 10%)',
  borderRadius: '8px',
  display: 'grid',
  gap: '16px',
});


const ListItem = styled(motion.li, {
  minWidth: '300px',
  background: 'hsla(222, 89%, 65%, 10%)',
  boxShadow: '0 0px 10px -6px rgba(0, 24, 40, 0.3)',
  borderRadius: '8px',
  padding: '8px',
  display: 'flex',
  alignItems: 'center',
  justifyContent: 'space-between',
  cursor: 'pointer',
  marginBottom: '0px',
  color: 'hsl(223, 15%, 65%)',
  fontSize: 18,
});

const Button = styled('button', {
  background: 'transparent',
  cursor: 'pointer',
  border: 'none',
  shadow: 'none',
  color: 'hsl(223, 15%, 65%)',
  display: 'flex',
});

const InfoBox = styled('div', {
  width: '50%',
});

const FilterWrapper = styled('div', {
  marginBottom: '16px',
  input: {
    marginRight: '4px',
  },
  label: {
    marginRight: '4px',
  },
});

const Title = motion.div;

const ARTICLES = [
  {
    category: 'swift',
    title: 'Intro to SwiftUI',
    description: 'An article with some SwitftUI basics',
    id: 1,
  },
  {
    category: 'js',
    title: 'Awesome React stuff',
    description: 'My best React tips!',
    id: 2,
  },
  {
    category: 'js',
    title: 'Styled components magic',
    description: 'Get to know ways to use styled components',
    id: 3,
  },
  {
    category: 'ts',
    title: 'A guide to Typescript',
    description: 'Type your React components!',
    id: 4,
  },
];

const categoryToVariant = {
  js: 'warning',
  ts: 'info',
  swift: 'danger',
};

const Item = (props) => {
  const { article, showCategory, expanded, onClick } = props;

  const readButtonVariants = {
    hover: {
      opacity: 1,
    },
    initial: {
      opacity: 0,
    },
  };

  return (
    <ListItem layout initial="initial" whileHover="hover" onClick={onClick}>
      <InfoBox>
        {}
        <Title
        
        >
          {article.title}
        </Title>
        <AnimatePresence>
          {expanded && (
            <motion.div
              style={{ fontSize: '12px' }}
              initial={{ opacity: 0 }}
              animate={{ opacity: 1 }}
              exit={{ opacity: 0 }}
            >
              {article.description}
            </motion.div>
          )}
        </AnimatePresence>
      </InfoBox>
      <AnimatePresence>
        {showCategory && (
          <motion.div
            layout
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
          >
            <Pill variant={categoryToVariant[article.category]}>
              {article.category}
            </Pill>
          </motion.div>
        )}
      </AnimatePresence>
      <motion.div
        layout
        variants={readButtonVariants}
        transition={{ duration: 0.25 }}
      >
        <Button
          aria-label="read article"
          title="Read article"
          onClick={(e) => e.preventDefault()}
        >
          &#8594;
        </Button>
      </motion.div>
    </ListItem>
  );
};

const Component = () => {
  const [showCategory, setShowCategory] = React.useState(false);
  const [sortBy, setSortBy] = React.useState('title');
  const [expanded, setExpanded] = React.useState(null);

  const onSortChange = (event) => setSortBy(event.target.value);

  const articlesToRender = ARTICLES.sort((a, b) => {
    const itemA = a[sortBy].toLowerCase();
    const itemB = b[sortBy].toLowerCase();

    if (itemA < itemB) {
      return -1;
    }
    if (itemA > itemB) {
      return 1;
    }
    return 0;
  });

  return (
    <>
      <FilterWrapper>
        <div>
          <input
            type="checkbox"
            id="showCategory2"
            checked={showCategory}
            onChange={() => setShowCategory((prev) => !prev)}
          />
          <label htmlFor="showCategory2">Show Category</label>
        </div>
        <div>
          Sort by:{' '}
          <input
            type="radio"
            id="title"
            name="sort"
            value="title"
            checked={sortBy === 'title'}
            onChange={onSortChange}
          />
          <label htmlFor="title">Title</label>
          <input
            type="radio"
            id="category"
            name="sort"
            value="category"
            checked={sortBy === 'category'}
            onChange={onSortChange}
          />
          <label htmlFor="category">Category</label>
        </div>
      </FilterWrapper>
      {}
      <LayoutGroup>
        <List layout>
          {articlesToRender.map((article) => (
            <Item
              key={article.id}
              expanded={expanded === article.id}
              onClick={() => setExpanded(article.id)}
              article={article}
              showCategory={showCategory}
            />
          ))}
        </List>
      </LayoutGroup>
    </>
  );
};

export default Component;

Conclusion

Congrats, you are now a Framer Motion expert 🎉! From propagating animations to orchestrating complex layout animations, we just went through some of the most advanced patterns that the library provides. We saw how well designed some of the tools provided are, and how easy it is thanks to those to implement complex transitions that would usually require either much more code or end up having a lot more undesirable side effects.

I really hope the examples provided in this blog post helped illustrate concepts that would otherwise be too hard to describe by text and that, most importantly, were fun for you to play with. As usual, do not hesitate to send me feedback on my writing, code, or examples, I'm always striving to improve this blog!

Did you come up with some cool animations after going through this guide?

Don't hesitate to send me a message showcasing your creations!

Want to see more examples?

The Framer Motion documentation has tons of those to play with on Codepen.

If you want to dig a bit deeper, below is the list of links to check out the implementations of the widgets featured in this article: