How to scroll to top in react

Learn how to scroll to top in react

Sometimes when page height gets very long, we provide an option to user to scroll back to top at click rather than scrolling back.

React scroll to top

We will create a similar component in react which will become visible after user has vertically scrolled to some distance.

I assume you have got a better idea about what we are going to build, so let us begin with the development.

For our help we will using few external packages.

  • prop-types: To validate the props.
  • classnames: This helps to use CSS classes as javascript objects, we just need to name our CSS file as filename.module.css

First, import all the required packages and setup the structure of the component.

import React, { Component } from "react";
import PropTypes from "prop-types";
import cx from "classnames";
import styles from "./index.module.css";

class ScrollTop extends Component {
   //Other code will go here
}

export default ScrollTop;

Validate the props at the start, this helps to get a good idea about the way in which we have to build this component and how its behavior will be.

Also if the required props are missing then set the default.

  static propTypes = {
    scrollStepInPx: PropTypes.number.isRequired,
    delayInMs: PropTypes.number.isRequired,
    placement: PropTypes.oneOf(["left", "right"]).isRequired,
    showAfterInPx: PropTypes.number.isRequired
  };

  static defaultProps = {
    scrollStepInPx: 50,
    delayInMs: 16.66,
    placement: "right",
    showAfterInPx: 100
  };

These are the props that we are expecting.

  • showAfterInPx for when to show the back to top button.
  • placement of the button at the bottom of the screen.
  • scrollStepInPx to decide the no of px should scroll to top at one instance.
  • delayInMs speed at which it should scroll back.

No one likes the jerking effect on their website, if you clicked on the button and it goes to the top instantly that is not a good user experience.

So the last two props helps to scroll back smoothly.

We have created the stateful component because we need to maintain the state of how much user has scrolled in order to show the back to top button.

To get the scroll distance we will have to listen to the scroll event and then store it. The best place to assign the event listeners is in the lifecycle methods.

  state = {
    scrolled: 0
  };

  componentDidMount() {
    window.addEventListener("scroll", this.trackScrollPosition);
  }

  componentWillUnmount() {
    window.removeEventListener("scroll", () => {});
  }

  //How much page is scrolled
  trackScrollPosition = () => {
    this.setState({
      scrolled: window.pageYOffset
    });
  };

This will keep on updating the state as user keeps scrolling.

Next generate the layout.

  render() {
    const { scrolled } = this.state;
    const { placement, showAfterInPx } = this.props;

    //If not in visibility the return null
    if (scrolled < showAfterInPx) {
      return null;
    }

    //Render the button
    return (
      <span
        title="Back to top"
        className={cx(styles.scroll, styles[placement])}
        onClick={this.scrollToTop}
      >
        Top
      </span>
    );
  }

If user has not scrolled till the distance at which we have to show the back to top button then return null. Falsey values render nothing in react.

Else generate the button. On the click of this button the window will scroll to top.

Now the last thing pending is creating the function to scroll to top.

  //Scroll step by step
  scrollStep = () => {
    const { scrollStepInPx } = this.props;
    if (window.pageYOffset === 0) {
      clearInterval(this.timer);
    }
    window.scroll(0, window.pageYOffset - scrollStepInPx);
  };

  //Start scrolling to top
  scrollToTop = () => {
    const { delayInMs } = this.props;

    this.timer = setInterval(() => {
      this.scrollStep();
    }, delayInMs);
  };

  componentWillUnmount() {
    clearInterval(this.timer);
  }

We are basically calling the function at an interval which will scroll at given distance with given speed.

Once it has reached to top clear the interval.

If component is about unmount in then also clear the interval.

Complete code of scroll to top in react.

import React, { Component } from "react";
import PropTypes from "prop-types";
import cx from "classnames";
import styles from "./index.module.css";

class ScrollTop extends Component {
  state = {
    scrolled: 0
  };

  static propTypes = {
    scrollStepInPx: PropTypes.number.isRequired,
    delayInMs: PropTypes.number.isRequired,
    placement: PropTypes.oneOf(["left", "right"]).isRequired,
    showAfterInPx: PropTypes.number.isRequired
  };

  static defaultProps = {
    scrollStepInPx: 50,
    delayInMs: 16.66,
    placement: "right",
    showAfterInPx: 100
  };

  componentDidMount() {
    window.addEventListener("scroll", this.trackScrollPosition);
  }

  componentWillUnmount() {
    window.removeEventListener("scroll", () => {});
  }

  //How much page is scrolled
  trackScrollPosition = () => {
    this.setState({
      scrolled: window.pageYOffset
    });
  };

  //Scroll step by step
  scrollStep = () => {
    const { scrollStepInPx } = this.props;
    if (window.pageYOffset === 0) {
      clearInterval(this.timer);
    }
    window.scroll(0, window.pageYOffset - scrollStepInPx);
  };

  //Start scrolling to top
  scrollToTop = () => {
    const { delayInMs } = this.props;

    this.timer = setInterval(() => {
      this.scrollStep();
    }, delayInMs);
  };

  componentWillUnmount() {
    clearInterval(this.timer);
  }

  render() {
    const { scrolled } = this.state;
    const { placement, showAfterInPx } = this.props;

    //If not in visibility the return null
    if (scrolled < showAfterInPx) {
      return null;
    }

    //Render the button
    return (
      <span
        title="Back to top"
        className={cx(styles.scroll, styles[placement])}
        onClick={this.scrollToTop}
      >
        Top
      </span>
    );
  }
}

export default ScrollTop;

Complete style code of react scroll to top

//index.module.css
.scroll {
  position: fixed;
  width: 60px;
  height: 60px;
  border-radius: 5px;
  bottom: 20px;
  background-color: red;
  color: #fff;
  display: flex;
  justify-content: center;
  align-items: center;
  cursor: pointer;
}

.left {
  left: 20px;
}

.right {
  right: 20px;
}

Input

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import ScrollTop from "./Components/ScrollTop";
import * as serviceWorker from "./serviceWorker";

const divs = ["purple", "green", "yellow", "blue"].map(e => (
  <div key={e} style={{ height: "100vh", backgroundColor: e }}></div>
));

ReactDOM.render(
  <div className="abc">
    {divs}
    <ScrollTop />
  </div>,
  document.getElementById("root")
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();