DEV Community

loading...

Simple React fade animation hook

ekeijl profile image Edwin ・2 min read

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!

Discussion (0)

pic
Editor guide