Layout Group

Documentation

Layout Group

Reading Time:

2

min

Released

October 2, 2023

Group motion components that should perform layout animations together.

By default, motion components with a layout prop will attempt to detect and animate layout changes every time they commit a React render.

It might be the case that components in different trees affect each other's layout.

function ToggleContent({ header, content }) {
  const [isOpen, setIsOpen] = useState(false)
  
  return (
    <motion.div
      layout
      onClick={() => setIsOpen(!isOpen)}
    >
      <motion.h2 layout>{header}</motion.h2>
      {isOpen ? content : null}
    </motion.div>
  )
}

function App() {
  return (
    <>
      <ToggleContent />
      <ToggleContent />
    </>  
  )
}

When the state of one of these ToggleContent components changes, its sibling won't rerender, so it won't perform a layout animation.

This can be fixed by grouping both components with LayoutGroup:

import { LayoutGroup } from "framer-motion"

function App() {
  return (
    <LayoutGroup>
      <ToggleContent />
      <ToggleContent />
    </LayoutGroup> 
  )
}

Now, whenever one layout component within LayoutGroup detects and animates layout changes, they all do.


Namespace layoutId

Components expecting to perform shared layout animations are provided a layoutId prop.

function Tab({ label, isSelected }) {
  return (
    <li>
      {label}
      {isSelected
        ? <motion.div layoutId="underline" />
        : null}
    </li>  
  )
}

function TabRow({ items }) {
  return items.map(item => <Tab {...item} />)
}

layoutId is global, so if multiple instances of TabRow are rendered, only one with layoutId="underline" will render at a time.

To fix this, LayoutGroup can be provided an id prop that namespaces the layoutId for all components within it.

function App() {
  return (
    <LayoutGroup id="top5">
      <TabRow />
    </LayoutGroup>  
    <LayoutGroup id="latest">
      <TabRow />
    </LayoutGroup>  
  )
}

Istanbul

Follow us on social media