Layout Animation In Framer Motion

Layout animations in Framer Motion allow you to create smooth transitions when the layout of your components changes. This is particularly useful for dynamic UIs where elements may be added, removed, or reordered.

How it works

Framer Motion provides a layout prop that can be added to motion components. When the layout of these components changes (due to state changes, window resizing, etc.), Framer Motion automatically animates the transition between the old and new layouts which means we have initial and final position of the element and framer motion will take care of animating between those two positions like morph animation.

Setting Up Layout Animations

To set up layout animations in Framer Motion, follow these steps:

  1. Import Modules: Import the necessary modules from Framer Motion.
  2. Wrap Components: Wrap the components you want to animate with motion elements.
  3. Add Layout Prop: Add the layout prop to the motion components that should animate on layout changes.
  4. Trigger Changes with State: Use state management to trigger layout changes dynamically.

Note: Always apply layout changes through styles for proper animation. Note: The layout animation doesnt work with inline elements like span, strong etc. It works with block elements like div, section, article etc. so if you are working with inline elements then make sure to change their display property to block or inline-block.

Example of Layout Animation

import React from "react";
import { AnimatePresence, easeIn, motion } from "framer-motion";
import { Send } from "lucide-react";

const App = () => {
  const [open, setIsOpen] = React.useState(false);
  return (
    <motion.div className="h-screen bg-black  mx-auto text-black flex items-center justify-center">
      <div className="flex space-x-2.5 w-1/3">
        <input
          className="outline-none grow bg-white p-3 rounded-2xl"
          style={{
            width: open ? "100%" : "0%",
            opacity: open ? 1 : 0,
            padding: open ? "0.75rem" : "0",
          }}
        />

        <motion.button
          style={{
            width: open ? "20%" : "100%",
          }}
          layout
          onClick={() => setIsOpen(!open)}
          className="bg-white p-3 rounded-2xl cursor-pointer"
        >
          <motion.span className="flex space-x-5" layout>
            <motion.span layout>
              <Send />
            </motion.span>

            {!open && (
              <motion.span
                layout
                initial={{ opacity: 0, x: -15 }}
                animate={{ opacity: 1, x: 0 }}
                exit={{ opacity: 0, x: -15 }}
                transition={{
                  opacity: {
                    type: "spring",
                  },
                }}
              >
                Send a message
              </motion.span>
            )}
          </motion.span>
        </motion.button>
      </div>
    </motion.div>
  );
};

export default App;

Another Example

import React from "react";
import { motion } from "framer-motion";
const App = () => {
  const [toggle, setToggle] = React.useState(true);
  return (
    <div className="w-full h-screen flex justify-center items-center">
      <motion.div
      layout
        className="bg-pink-400 flex px-10 py-3 space-x-4 rounded-xl space-y-4 items-center"
        style={{
          flexDirection: toggle ? "row" : "column",
          padding: toggle ? "20px 40px" : "40px 40px",
        }}
      >
        <motion.video
        layout
          src="https://media.istockphoto.com/id/1330803052/video/seamless-loop-blue-neon-glowing-loading-bar-progress-download-circle-on-black-background.mp4?s=mp4-640x640-is&k=20&c=yuolHV_L9r1u2jFIGj89kWbrNGBy1sYu5Lrm540IuVo="
          autoPlay
          loop
          muted
          className=" h-10 rounded-full"
        ></motion.video>

        <motion.div layout className="flex flex-col ">
          <motion.h2 layout className="font-bold">Playing now</motion.h2>
          <motion.span layout>Dil ki darya</motion.span>
        </motion.div>
      </motion.div>
      <button
        onClick={() => {
          setToggle((s) => !s);
        }}
        className="absolute bottom-0  px-5 rounded-xl cursor-pointer py-4 mb-10 bg-pink-400 border-none"
      >
        Toggle
      </button>
    </div>
  );
};

export default App;

Animating the border radius with layout animation

If we want to animate the border radius of a component in layout animation animate it using animate prop if you doesnt want to change the border radius then simply add the border radius inside the style object.

Example1:

<motion.div
  layout

  animate={{
    borderRadius: isOpen ? "20px" : "50%",
  }}>
    Box
</motion.div>

Example2:

<motion.div
  layout
  style={{
    borderRadius: "20px",
  }}>
    Box
</motion.div>

Variations of layout prop

  • layout: This will animate the position and size changes of the component.
  • layout="position": This will animate only the position changes of the component, not the size.
  • layout="size": This will animate only the size changes of the component, not the position.
  • layout= "preserve-aspect": This will animate the size changes of the component while preserving its aspect ratio.