DEV Community

Cover image for React Component to Smooth Scroll to the Top
Pranav Birajdar
Pranav Birajdar

Posted on • Edited on

React Component to Smooth Scroll to the Top

It's kinda rare to find a smooth scrolling button that takes you to the top of the page on modern blogging websites, especially the ones that are a long 15 minute read!

However, whenever I come across one, I always tend to use it and appreciate the elegance of this simple button that has such a specific job.

After perusing Stack Overflow and GitHub for solution, I came across an elegant React component that uses Hooks and wanted to share it with this community!

Our button should function like this:
Alt Text

These are following test-cases for our component:

  • Button should always be at the right bottom of the page
  • Button should be hidden and should only appear when we scroll for a certain height
  • On clicking it, we should be smoothly taken to the top of the page

The Hook component achieves the following functionality.



import React, { useEffect, useState } from "react";

export default function ScrollToTop() {
  const [isVisible, setIsVisible] = useState(false);

  // Top: 0 takes us all the way back to the top of the page
  // Behavior: smooth keeps it smooth!
  const scrollToTop = () => {
    window.scrollTo({
      top: 0,
      behavior: "smooth"
    });
  };

  useEffect(() => {
    // Button is displayed after scrolling for 500 pixels
    const toggleVisibility = () => {
      if (window.pageYOffset > 500) {
        setIsVisible(true);
      } else {
        setIsVisible(false);
      }
    };

    window.addEventListener("scroll", toggleVisibility);

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

//scroll-to-top classes: fixed, bottom:0, right:0
  return (
    <div className="scroll-to-top">
      {isVisible && (
        <div onClick={scrollToTop}>
          <h3>Go up!</h3>
        </div>
      )}
    </div>
  );
}


Enter fullscreen mode Exit fullscreen mode

We're almost done! Just import this component in your react file and stick it at the very end.

And voila, it should work!

Here is a basic, quick, and ugly demo of how it should function!

Top comments (25)

Collapse
 
darkwiiplayer profile image
𒎏Wii 🏳️‍⚧️

I had to build a similar thing a while ago, but without any framework; ended up with much less code by simply adding a scroll event handler that toggles a top class on the body element. The CSS then just has a rule like .top .scroll-to-top { opacity: 1 }

Collapse
 
prnvbirajdar profile image
Pranav Birajdar

Damn! That's awesome. Do you have a demo by any chance?

Collapse
 
darkwiiplayer profile image
𒎏Wii 🏳️‍⚧️

cc-entrance.comcard.de Ignore the fact that it's in German 😁

Also there's a few additional effects other than just fading in and out, which obviously makes the CSS a bit more complex.

Thread Thread
 
prnvbirajdar profile image
Pranav Birajdar • Edited

This looks so good! I just looked at the event code and the underlying function is almost the same. It's just less code due to being written in pure HTML and CSS I reckon. Correct me if I am wrong!

Thread Thread
 
darkwiiplayer profile image
𒎏Wii 🏳️‍⚧️

Yea, I don't think the way I do it is all that different from yours, the only detail that really changes is that I use more of the CSS cascade in an attempt to make it more "generic" :D

Collapse
 
link2twenty profile image
Andrew Bone

This is pretty cool and a great way to get people up and running fast there's one minor change I'd make though.

import { useEffect, useState } from "react";

export default function ScrollToTop() {
  const [isVisible, setIsVisible] = useState(false);

  // Top: 0 takes us all the way back to the top of the page
  // Behavior: smooth keeps it smooth!
  const scrollToTop = () => {
    window.scrollTo({
      top: 0,
      behavior: "smooth"
    });
  };

  useEffect(() => {
    // Button is displayed after scrolling for 500 pixels
    const toggleVisibility = () => {
      if (window.pageYOffset > 500) {
        setIsVisible(true);
      } else {
        setIsVisible(false);
      }
    };

    window.addEventListener("scroll", toggleVisibility);

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

  return (
    <div className="scroll-to-top">
      {isVisible && (
        <div onClick={scrollToTop}>
          <h3>Go up!</h3>
        </div>
      )}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

By moving the function into the useEffect it is only declared once rather than on every draw. Also by adding a return to that same useEffect we can stop listening if the component is ever unmounted.

As an interesting side point you could look into using IntersectionObserver, with a scroll event fallback, if you wanted to improve on performance, but I don't think this would really need it 😉

Collapse
 
prnvbirajdar profile image
Pranav Birajdar

Thank you for pointing out the cleanup for useEffect. I always tend to forget that. I had not considered IntersectionObserver since I didn't know there was a performance hit, but I will certainly look into it!

Collapse
 
bramus profile image
Bramus!

You don't need JS for this, as you achieve this with only CSS.

html {
  scroll-behavior: smooth;
}
Enter fullscreen mode Exit fullscreen mode

☝️ If you now link to #top, your browser will smoothscroll to the top of the page (you don't even need an anchor/element with that name)

DONE. 😱

(Not supported in Safari though — Meh 😕)

Collapse
 
duongductrong profile image
Trong Duong • Edited
window.scrollTo({
      top: 0,
      behavior: "smooth"
    });
Enter fullscreen mode Exit fullscreen mode

not working on safari browser. can u fix it?

Collapse
 
prnvbirajdar profile image
Pranav Birajdar

Since I use Chrome and Firefox, I didn't know it doesn't work on Safari.

This post on Stack Overflow has some suggestions you might find helpful.
stackoverflow.com/questions/560112...

Collapse
 
duongductrong profile image
Trong Duong

helpful ! thank bro

Collapse
 
pompolutz profile image
Oleh Lutsenko

And of course you want to remove event listener as well? :)

Collapse
 
prnvbirajdar profile image
Pranav Birajdar

Just updated the code. Thank you for the comment!

Collapse
 
ricardobeat profile image
Ricardo Tomasi • Edited

Just like back navigation, this functionality is in fact so important that every computer already has a physical button for it: the "Home" key, or shift+space / cmd+up if you don't have one.

Duplicating system or browser behaviour is simply introducing UI inconsistency - not a fan of these.

Collapse
 
prnvbirajdar profile image
Pranav Birajdar • Edited

Damn, two decades of using computers and I didn't know a 'Home' key already does that for you! 🤯

Collapse
 
prnvbirajdar profile image
Pranav Birajdar

Thank you for the feedback. I tried to get a basic Code Sandbox up and running but Chrome kept on crashing on me.

And the example I used is from my portfolio that I am currently working on. I will post a link as soon as it's live!

Collapse
 
hey_yogini profile image
Yogini Bende

Simple yet very useful tutorial. Thanks for sharing 🙌

Collapse
 
prnvbirajdar profile image
Pranav Birajdar

Glad you found it useful!

Collapse
 
emmgfx profile image
Josep Viciana

Nice, but I think hooks should start with use, like useScrollToTop.

Collapse
 
prnvbirajdar profile image
Pranav Birajdar

You're right. I think this can be called a Scroll component then! 😅

Collapse
 
sni9er profile image
Ahmed-Mohammed-Badawi

Nice job bro you helped a lot but can u tell me why this line ?

return () => window.removeEventListener("scroll", toggleVisibility);
Enter fullscreen mode Exit fullscreen mode