DEV Community

Cover image for Scroll to top button with React + Framer Motion
Daniel Castro
Daniel Castro

Posted on

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
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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

.arrowUp {
  width: 2rem;
  height: 2rem;
}
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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";
Enter fullscreen mode Exit fullscreen mode
//...
  return (
    <motion.button // <--- change here
      className="scrollToTop-btn"
      onClick={goToTop}
    >
      {arrowUp}
    </motion.button> // <--- and change here
  );
}
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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";
Enter fullscreen mode Exit fullscreen mode

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);
  }, []);
//...
Enter fullscreen mode Exit fullscreen mode

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

  return (
    {scrollPosition > 100 && (
      <motion.button
      //...
      </motion.button>
    )}
  );
Enter fullscreen mode Exit fullscreen mode

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";
Enter fullscreen mode Exit fullscreen mode
return (
    <AnimatePresence>
      {scrollPosition > 100 && (
        <motion.button
        //...
        </motion.button>
      )}
    </AnimatePresence>
  );
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)