imran.jpg

Imran

Posted

Oct 10

Animate Children Height Dynamically with react-spring

Our goal

To animate a container that collapses its children smoothly.

Use cases:

  • Accordian component

What wont work

When:

  • animate with percentage based height value

  • apply scaleY to the animated div

  • positioned component

  • display "none"

Lets start with fixed height

FixedHeight component has a parent container that hides its child item of height: 200px.


import { animated, useSpring } from "@react-spring/web";
import { useState } from "react";
import Item from "./Item";
import "./app.css";

export default function FixedHeight() {
const [open, setOpen] = useState(true);
const styles = useSpring({
  opacity: open ? 1 : 0,
  height: open ? 200 : 0, // fixed height
  pointerEvents: open ? "auto" : "none",
  cursor: open ? "auto" : "default",
  overflow: "hidden",
});
return (
  <div className="container">
    <div className="flex-box">
      <p>The quick brown box hides the blue box.</p>
      <button onClick={() => setOpen((prev) => !prev)}>
        {open ? "Hide" : "Show"}
      </button>
    </div>

    <animated.div className="item" style={{ ...styles }}>
      <Item />
    </animated.div>
  </div>
);
}

When the children component height is unknown

Setting the height to percentage based value don't produce the result we look for since the browser will just ignore and default to the children's height value. This leads to the parent element stays static in size while the child element disappear due to the rest of animation properties took effect except for height.

So, our solution would simply by measuring the child element DOM size with browser DOMRect API.


import Switcher from "./Switcher";
import "./app.css"

export default function App() {
return <Switcher />;
}

What made it works

By measuring the child item DOM size in its parent element with Javascript. At the time of writing, I could not find simpler solution using CSS height values alone. If you do, please reach me out on twitter or join my livestream.

Discussion

Although above solution works, we do need to measure if the child element updates its size.

Our options: