React: How to animate removal of a DOM element?

Viewed 4835

I have a banner my React component, to which I want to add "dismiss" capability. The current implementation looks like:

function Home() {
  return(
    <Banner />
    <Main />
  );
}

function Banner() {
  const [hide, setHide] = React.useState(false);
  const handleClick = () => setHide(true);
  return !hide ? (
    <div>
      This is a banner.
      <button onClick={handleClick}>Dismiss</button>
    </div> : null;
}

However, this implementation is rather abrupt and I can't animate it. Is there a better way to avoid jumping of the elements to provide a better UI?

1 Answers

My preferred choice for React animations is the react-transition-group library. (There are of course other options). Here is my Stackblitz demo

It's fairly easy to use. You wrap the component you want to animate in the CSSTransition component, add some config properties, and set your styles in a style sheet. One slight gotcha is that the timeout property has to be the same value as the transition times in the stylesheets. See documentation for more information.

Here is the most relevant code from the demo:

App Component

export function App() {
  const [showBanner, setShowBanner] = useState(true);
  const hideBannerHandler = () => setShowBanner(false);
  const showBannerHandler = () => setShowBanner(true);

  return (
    <div>
      <button onClick={showBannerHandler}>show banner</button>
      <CSSTransition
        in={showBanner}
        timeout={{
          enter: 0,
          exit: 2000
        }}
        unmountOnExit
        classNames="my-node"
      >
        <Banner hideBannerHandler={hideBannerHandler} />
      </CSSTransition>
    </div>
  );
}

CSS (fragment)


.my-node-exit {
  opacity: 1;
}

.my-node-exit-active {
  animation: foo 2s ease forwards;
  transform-origin: left;
}

@keyframes foo {
  from {
    opacity: 1;
    background: pink;
    transform: translate(0, 0);
    box-shadow: 0 0 2px 3px pink;
    color: red;
  }

  50% {
    transform: scale(2);
    background: coral;
    box-shadow: 0 0 2px 3px coral;
    color: limegreen;
  }

  to {
    opacity: 0;
    transform: scale(1);
    background: white;
    color: white;
  }
}
Related