85
Scroll to top button with React + Framer Motion
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
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>
);
}
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;
}
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;
}
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>
);
}
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.
Add the dependency by running the following command.
npm install framer-motion
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
);
}
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>
);
}
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>
)}
);
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>
);
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