Scroll to top button with React + Framer Motion

TLDR

If you are like me and you want to get straight to the code, just look at the final result in the code sandbox here.
Remember to add Framer Motion to your react dependencies.

npm install framer-motion

ScrollToTop Button

We'll create our simple button that, for now, will always appear on the screen.
Later, on section Scroll progress, we'll change to only appear when we scroll down a bit.

import "./styles.css";

export default function ScrollToTop() {
  return (
    <button
      className="scrollToTop-btn"
    >
      Click Me!
    </button>
  );
}

Style

Since the button looks too raw, we'll add some styling to the imported styles file.
We already imported the styles and added a className to the button.
You can customize your button as you want, be creative!
Here is an example of a styled button.

.scrollToTop-btn {
  display: flex;
  justify-content: center;
  align-items: center;

  position: fixed;
  bottom: 1.5rem;
  right: 1.5rem;
  width: 3.5rem;
  height: 3.5rem;
  font-size: 1rem;
  line-height: 3rem;

  background-color: #007acc;
  border: none;
  border-radius: 50%;
  color: white;
  cursor: pointer;
}

Icon

Now, We'll change the button content to be an intuitive icon.
There are a lot of websites that provide icons. We'll use Font Awesome, but feel free to use the one that you like the most.
We'll extract the SVG code. An SVG is just a bunch of vetors that form an image (check this link for a more precise definition).

//...

  const arrowUp = (
    <svg
      className="arrowUp" // <-- add this for styling
      aria-hidden="true"
      focusable="false"
      data-prefix="fas"
      data-icon="chevron-up"
      role="img"
      xmlns="http://www.w3.org/2000/svg"
      viewBox="0 0 448 512"
    >
      <path
        fill="currentColor"
        d="M240.971 130.524l194.343 194.343c9.373 9.373 9.373 24.569 0 33.941l-22.667 22.667c-9.357 9.357-24.522 9.375-33.901.04L224 227.495 69.255 381.516c-9.379 9.335-24.544 9.317-33.901-.04l-22.667-22.667c-9.373-9.373-9.373-24.569 0-33.941L207.03 130.525c9.372-9.373 24.568-9.373 33.941-.001z"
      ></path>
    </svg>
  );

  return (
    <button
      className="scrollToTop-btn"
    >
      {arrowUp} // <--- change here
    </button>
  );
}

We'll add a width and height to the icon.

.arrowUp {
  width: 2rem;
  height: 2rem;
}

onClick

The visuals of the button are completed!
We'll add the button logic with the goToTop function.
The goToTop function scrolls into the beginning of the page, top: 0, with a more natural and smooth behavior, behavior: "smooth".

//...

  // Add the smooth behavior to go to top
  const goToTop = () => {
    document.documentElement.scrollTo({
      top: 0,
      behavior: "smooth"
    });
  };

  return (
    <button
      className="scrollToTop-btn"
      onClick={goToTop} // <--- add this
    >
      {arrowUp}
    </button>
  );
}

Framer Motion

Now that we have our nice button working, we'll add some motion πŸ’ƒ to it.
Animations are a great way for your components to stand out! But be careful and do not over do it. Trust me, I have gone through that track. πŸ˜…

For that we'll use Framer Motion. Framer Motion is a Production-Ready Animation Library for React. It enables the creation of animations with much ease than doing them with plain CSS.

Installation

Add the dependency by running the following command.

npm install framer-motion

Convert button into motion.button

Then, to start using motion, import motion and change the button tag to a motion.button tag.

import { motion } from "framer-motion";
//...
  return (
    <motion.button // <--- change here
      className="scrollToTop-btn"
      onClick={goToTop}
    >
      {arrowUp}
    </motion.button> // <--- and change here
  );
}

Motion animations & transitions

And voila, is that simple!
Now, we can use the Framer Motion Library API and add animations and transitions to the button.

We'll add all the animations now. Their names are almost self explanatory but I advise you to check Framer Motion documentation for more info on them, and to learn new and interesting ways of adding animations to your components.
They have a really intuitive page, where you can see the major animations you can do accomplish with Framer Motion.

We added an initial, animate, and exit property to animate the entry and exit of the button. We also added an animation while hovering and tapping/clicking the button.

//...
  return (
    <motion.button
      className="scrollToTop-btn"
      onClick={goToTop}
      initial={{ y: 100, opacity: 0 }}
      animate={{ y: 0, opacity: 1,
                 transition: { duration: 0.6 } }}
      exit={{ y: 100, opacity: 0,
                 transition: { duration: 0.6 } }}

      whileHover={{
        scale: 1.2,
        transition: { duration: 0.2 }
      }}

      whileTap={{ scale: 1 }}
    >
      {arrowUp}
    </motion.button>
  );
}

Scroll progress

With the animations completed, it is missing the scroll progress to check when the button should appear.

First, we'll import useState and useEffect from react.

import { useState, useEffect } from "react";

Second, we'll create this scrollPosition state that will have the position of the Y/height of the scrollbar.

/...
  const [scrollPosition, setScrollPosition] = useState(0);

  useEffect(() => {
    const updatePosition = () => {
      setScrollPosition(window.pageYOffset);
    };

    window.addEventListener("scroll", updatePosition);

    return () => window.removeEventListener("scroll", updatePosition);
  }, []);
//...

Third and final, we'll show the motion button conditionally, with the scrollPosition state

return (
    {scrollPosition > 100 && (
      <motion.button
      //...
      </motion.button>
    )}
  );

Motion AnimatePresence

Finally, to wrap things up, we'll use AnimatePresence to be able to use the exit property. When we are at the top of the page, the button will exit gracefully.

import { motion, AnimatePresence } from "framer-motion";
return (
    <AnimatePresence>
      {scrollPosition > 100 && (
        <motion.button
        //...
        </motion.button>
      )}
    </AnimatePresence>
  );

Final result

If you wanna see the full final version, check the code sandbox here.
I am a constantly learning person and because of that, I encourage everyone to make suggestions!
I hope you enjoyed this scrollToTop component with motion animations that you can add to your react project or web app.

85