DEV Community

Edwin
Edwin

Posted on

Simple React fade animation hook

Hey you! Yea you! Do you need a quick and simple fade animation on your React component? Don't feel like installing a library?
Then let's go! 💨💨💨

Show me the code already! 👀

The hook returns [isVisible, setVisible, fadeProps], just like useState() hook, but you also need to set the fadeProps on the element you want to fade.

const MyFadingComponent = () => {
    // Just like useState() hook, the fadeProps go on the fading DOM element
    const [isVisible, setVisible, fadeProps] = useFade();

    // You can use isVisible to mount/unmount the component!
    return <>
        <button onClick={() => setVisible(!isVisible)}>Toggle visibility</button>
        {isVisible && <h2 {...fadeProps}>Now you see me...</h2>}
    </>;
};
Enter fullscreen mode Exit fullscreen mode

And the hook! 🎣

It uses onAnimationEnd to delay setting the isVisible state to false, which allows the animation to finish before the component unmounts!

const useFade = (initial) => {
    const [show, setShow] = useState(initial);
    const [isVisible, setVisible] = useState(show);

    // Update visibility when show changes
    useEffect(() => {
        if (show) setVisible(true);
    }, [show]);

    // When the animation finishes, set visibility to false
    const onAnimationEnd = () => {
        if (!show) setVisible(false);
    };

    const style = { animation: `${show ? "fadeIn" : "fadeOut"} .3s` };

    // These props go on the fading DOM element
    const fadeProps = {
        style,
        onAnimationEnd
    };

    return [isVisible, setShow, fadeProps];
};
Enter fullscreen mode Exit fullscreen mode

Styles 💅

@keyframes fadeIn {
    0% { opacity: 0; }
    100% { opacity: 1; }
}

@keyframes fadeOut {
    0% { opacity: 1; }
    100% { opacity: 0; }
}
Enter fullscreen mode Exit fullscreen mode

What's the point? 🙄

If we would use the useState() hook and then apply the state with an expression like isVisible && <Component />, our component will unmount before the CSS animation has finished, which is not what we want! The useFade() hook delays unmounting until the animation is finished.

What's cool about this? 😎

The syntax is just like useState(), you can simply use an isVisible && <Component /> expression to mount/unmount the component.

Here's how you do it with React Transition Group, you need a wrapper component and wire the enter/exit animation to the state yourself, yuck! Framer Motion and React Spring are similar.

Room for improvement (please help!)

Toggling between two elements does not really work at the moment:

const [isVisible, setVisible, fromProps, toProps] = useFade();

{isVisible ? <ComponentA {...fromProps} /> : <ComponentB {...toProps} />}
Enter fullscreen mode Exit fullscreen mode

I'm trying to pass the opposite fade animation to ComponentB, but can't figure out how to get it right. If you have an idea, let me know!

Top comments (1)

Collapse
 
abhic7 profile image
Abhishek Chotaliya

Pro tip:
keep the default opacity to 0 for the component you're trying to animate. Otherwise, you'll see a flash kind of effect on fadeOut.