DEV Community

a-tonchev
a-tonchev

Posted on • Edited on

1

The simplest React mount/unmount animation with material UI and emotion

Surely you want sometimes in react to animate an entrance and/or exit of unmounting component. Instead of using a library, there is a nice way to do it by yourself, just with material ui!

For this we will make use of the emotion css prop and the keyframes helper.

As of material ui we can just take the Box component

Our goal is to create an Animated component that can receive following props:
show: if the component is mounted or not
mountData: describing the entrance animation
mountData.keyframes: Standard css animation keyframes
mountData.time: Animation duration in seconds
mountData.type: Css animation type (e.g. linear, ease-out...)
unmountData: describing the exit animation
unmountData.keyframes: Standard css animation keyframes
unmountData.time: Animation duration in seconds
unmountData.type: Css animation type (e.g. linear, ease-out...)
unmountTimeout (optional): to provide a possibility for auto unmount the component after a timeout
setShow (optional): function to unmount the component, provided by the parent

If you don't provide the last two, the parent component will control the whole mount/unmount process.

And here is the solution:

import { Box } from '@mui/material';
import { useEffect, useState } from 'react';
import { css, keyframes } from '@emotion/react';

const defaultMountData = {};

const Animated = ({
  children,
  show,
  setShow,
  mountData = defaultMountData,
  unmountData = defaultMountData,
  unmountTimeout,
  ...rest
}) => {
  const [animationData, setAnimationData] = useState(null);
  const { time, type = 'linear' } = animationData || {};

  const animationCss = animationData?.keyframes ?
    css`animation: ${keyframes`${animationData.keyframes}`} ${time}s ${type}`
    : '';

  useEffect(() => {
    let mounted = true;
    let handler = null;
    let unmountHandler = null;

    if (show) {
      setAnimationData(mountData);
      if (unmountTimeout && setShow) {
        unmountHandler = setTimeout(() => mounted && setShow(false), unmountTimeout);
      }
    } else if (animationData) {
      const { time: unmountTime } = unmountData;
      handler = setTimeout(() => mounted && setAnimationData(null), unmountTime * 1000);
      setAnimationData(unmountData);
    }

    return () => {
      handler && clearTimeout(handler);
      unmountHandler && clearTimeout(unmountHandler);
      mounted = false;
    };

    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [mountData, unmountData, show]);

  if (!animationData) return null;

  return (
    <Box
      css={animationCss}
      component="div"
      {...rest}
    >
      {children}
    </Box>
  );
};

export default Animated;
Enter fullscreen mode Exit fullscreen mode

We can not use default props for our default mount data, because it will cause all the time re-rendering.

Now in our component we place the mountData with the settings for entrance animation, the unmountData with settings for exit animation. As soon the show param become true, that will activate our Animated component entrance animation. After 4 seconds the exit animation will be played, and will set the shouldBeMounted variable to false, which will unmount the component:

const [shouldBeMounted, setShouldBeMounted] = useState(false);

<Animated
  show={shouldBeMounted}
  mountData={{
    keyframes: `
       0% {opacity: 0}
       100% {opacity: 1}
    `,
    time: 0.3,
  }}
  unmountData={{
    keyframes: `
      0% {opacity: 1}
      100% {opacity: 0}
    `,
    time: 0.8,
  }}
  unmountTimeout={4000}
  setShow={setShouldBeMounted}
>
  Text to hide with animation
</Animated>
Enter fullscreen mode Exit fullscreen mode

If we don't want automated unmounting, we can just ignore the unmountTimeout and the setShow params. If we don't want entrance or exit animation, we can also just ignore the mountData/unmountData:

const [shouldBeMounted, setShouldBeMounted] = useState(false);

<Animated
  show={shouldBeMounted}
  unmountData={{
    keyframes: `
      0% {opacity: 1}
      100% {opacity: 0}
    `,
    time: 0.8,
  }}
>
  Text to hide with animation
</Animated>
Enter fullscreen mode Exit fullscreen mode

Here, we control totally our Animated component from the parent, and we don't use any animation for the mount, we use animation only for unmount.

Well that's it!

This is a simple, fast and lightweight way to create mount animations just using css.

Best Regards
Anton Tonchev
JUST-SELL.online

Image of Datadog

The Essential Toolkit for Front-end Developers

Take a user-centric approach to front-end monitoring that evolves alongside increasingly complex frameworks and single-page applications.

Get The Kit

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Dive into an ocean of knowledge with this thought-provoking post, revered deeply within the supportive DEV Community. Developers of all levels are welcome to join and enhance our collective intelligence.

Saying a simple "thank you" can brighten someone's day. Share your gratitude in the comments below!

On DEV, sharing ideas eases our path and fortifies our community connections. Found this helpful? Sending a quick thanks to the author can be profoundly valued.

Okay