Implementing a Scroll To Top feature in React

The Window object provides a few methods that allow us to programmatically scroll around a web page, such as smooth scrolling to specific sections or returning the user to the top of the page.

Recently I used this on a project to create a "return to top" button that would appear once a user has begun scrolling down the page, and when clicked would quickly return the user to the top of the page.

Getting Started

To begin, we first need to register the element that we want to use as the target for the scroll action. For a "return to top" this could be the heading of the page, or any other element at the top that you choose. To register these elements using React Hooks, we will need to utilize the useRef hook to register the element.

First create the marker:

const topRef = useRef(null);

Second, attach it to the desired element using its ref attribute:

<div className="App">
      <h1 ref={topRef}>Scroll To Top Example</h1>
    </div>

Creating the Button

For the button itself, we can create a new component and assign a scrollToRef function to its click event. This function takes will accept the target ref, and use the scrollTo() function on the window object to scroll the window until the top of the ref element is visible. To make this action smooth, instead of an instantaneous jump, we can optionally pass a "behavior" property:

const scrollToRef = (target) => {
    window.scrollTo({ 
      top: target.current.offsetTop, 
      behavior: "smooth" 
    });
  }

Conditionally Rendering the Button

In my implementation, I wanted the button to only render once the user has scrolled a pre-defined distance down the page. To achieve this, we can utilize the scrollY property on the window object to determine how far down the page the user has scroll. With an event listener on the scroll event for the window, we can then compare the scrollY position of the window at each scroll to determine if the button's "show" state should be true or false. Alternatively, we could make this comparison on scroll start or scroll end to improve performance, but it would change its behavior.

Since the button will be mounted/unmounted conditionally, its important to remove the scroll event listener from the window object when the button is unmounted. To do this, we can return a cleanup function using the useEffect hook that will be invoked when the component un-mounts.

const GoToButton = ({ displayAfter, target }) => {
  const [showButton, setShowButton] = useState(false);
  const handleShowButton = () => {
    if (!showButton && window.scrollY > displayAfter) {
      setShowButton(true);
      return;
    }
    if (!showButton && window.scrollY <= displayAfter) {
      setShowButton(false);
      return;
    }
  };
  window.addEventListener("scroll", handleShowButton);

  useEffect(() => {
    return window.removeEventListener("scroll", handleShowButton);
  });

  const scrollToRef = (target) => {
    window.scrollTo({
      top: target.current.offsetTop,
      behavior: "smooth"
    });
  };

  if (showButton) {
    return <Button onClick={() => scrollToRef(target)}>TOP</Button>;
  } else {
    return "";
  }
};
export default GoToButton;

Conclusion

Similar approaches could be used to scroll down the page to different sections using a content menu and multiple section refs. The window object also has a scrollBy method which could be used in place of scrollTo if the desired behavior was to always scroll a specific distance, such as using window.scrollBy(0, window.innerHeight)
to scroll down by one page.

The code for the demonstration of this scroll to top feature in the animation can be found on this CodeSandbox

Resources:

14