Andrew Kovalenko
Andrew Kovalenko

Reputation: 6750

Scroll to the top of the page after render in react.js

I have a problem, which I have no ideas, how to solve. In my react component I display a long list of data and few links at the bottom. After clicking on any of this links I fill in the list with new collection of the links and need to scroll to the top.

The problem is - how to scroll to the top after new collection is rendered?

'use strict';

// url of this component is #/:checklistId/:sectionId

var React = require('react'),
  Router = require('react-router'),
  sectionStore = require('./../stores/checklist-section-store');


function updateStateFromProps() {
  var self = this;
  sectionStore.getChecklistSectionContent({
    checklistId: this.getParams().checklistId,
    sectionId: this.getParams().sectionId
  }).then(function (section) {
    self.setState({
      section,
      componentReady: true
    });
  });

    this.setState({componentReady: false});
 }

var Checklist = React.createClass({
  mixins: [Router.State],

  componentWillMount: function () {
    updateStateFromProps.call(this);
  },

  componentWillReceiveProps(){
    updateStateFromProps.call(this);
   },

render: function () {
  if (this.state.componentReady) {
    return(
      <section className='checklist-section'>
        <header className='section-header'>{ this.state.section.name }   </header>
        <Steps steps={ this.state.section.steps }/>
        <a href=`#/${this.getParams().checklistId}/${this.state.section.nextSection.Id}`>
          Next Section
        </a>
      </section>
    );
    } else {...}
  }
});

module.exports = Checklist;

Upvotes: 367

Views: 655895

Answers (30)

mostafa asadollahy
mostafa asadollahy

Reputation: 57

I found that most convenient way to get back in top if you are using react-router-dom v6.4 is to easily use ScrollRestoration import { ScrollRestoration } from "react-router-dom";

    function RootRouteComponent() {
  return (
    <div>
      <ScrollRestoration />
    </div>
  );
}

here is the link: https://reactrouter.com/en/main/components/scroll-restoration

Upvotes: 0

Apurv
Apurv

Reputation: 123

useEffect(() => {
    const body = document.querySelector('#root');

    body.scrollIntoView({
        behavior: 'smooth'
    }, 500)

}, []);

This best solution for view top scrolling page

or use ref to jump page

    // a link jump to page react
const ref = useRef(null);

  const handleClick = () => {
    ref.current?.scrollIntoView({behavior: 'smooth'});
  };

 <button onClick={handleClick}>Scroll to element</button>
 <div ref={ref}>Some content here</div>

In javascript, we can use window.scrollTo() function

 window.scrollTo({ top: 0, behavior: 'smooth' });

Upvotes: 3

Shubham Sarda
Shubham Sarda

Reputation: 635

For React v18+ my recommendation will be to use wrapper component, will be the easiest way to execute.

Step 1: Create a ScrollToTop component (component/ScrollToTop.js)

import { useEffect } from "react";
import { useLocation } from "react-router-dom";

export function ScrollToTop() {
  const { pathname } = useLocation();

  useEffect(() => {
    window.scrollTo(0, 0);
  }, [pathname]);

  return null;
}

Step 2: Wrap your App with it (index.js)

<React.StrictMode>
  <BrowserRouter>
    <ScrollToTop />
    <App />
  </BrowserRouter>
</React.StrictMode>

Explanation: Every time pathname changes useEffect will be called to scroll the page to the top.

Upvotes: 6

sahil singh
sahil singh

Reputation: 106

Smooth scroll to top . In hooks you can use this method inside lifecycle mounting state for once render

useEffect(() => {
  window.scrollTo({top: 0, left: 0, behavior: 'smooth' });
}, [])

Upvotes: 8

Akash Kumar Seth
Akash Kumar Seth

Reputation: 1701

I have tried @sledgeweight solution but it does not work well for some of the views. But adding a setTimeout seems to work perfectly. In case someone facing the same issue as me. Below is my code.

import { useEffect } from 'react'
import { useLocation } from 'react-router-dom'

const ScrollToTop = () => {
    const { pathname } = useLocation()
    useEffect(() => {
        console.log(pathname)
        /* settimeout make sure this run after components have rendered. This will help fixing bug for some views where scroll to top not working perfectly */
        setTimeout(() => {
            window.scrollTo({ top: 0, behavior: 'smooth' })
        }, 0)
    }, [pathname])
    return null
}

export default ScrollToTop

Use it in AppRouter.js as

<Router>
    <ScrollToTop/>
    <App>
</Router>

Upvotes: 12

Rohit Nishad
Rohit Nishad

Reputation: 2765

This solution is working for the Functional component as well as the Class Base.

First of all, I do not like the idea of Scroll to top on every re-render, instead, I like of attache function to the particular event.

Step #1: Create a function to ScrollToTop

const scrollToTop = () => {
    window.scrollTo({
        top: 0,
        behavior: "smooth",
    });
};

Step #2: Call this function on an event e.g onClick

onRowClick={scrollToTop()}
// onClick={scrollToTop()}
// etc...

Upvotes: 10

Khushi Shivde
Khushi Shivde

Reputation: 31

Solution for functional component - Using useEffect() hook

 useEffect(() => {
window.history.scrollRestoration = 'manual';}, []);

Upvotes: 3

VrisToK
VrisToK

Reputation: 69

I was doing a SPA in React 17.0 using functional components and window.scroll, window.scrollTo and all of this variants doesn't work for me. So I made a solution using useRef hook. I created a span tag in the top of the component with Ref and then I used and effect with ref.current.scrollIntoView()

There is a short example:

import React, { useEffect,useRef} from 'react';

export const ExampleComponent = () => {

  const ref = useRef();

  useEffect(() => {
      ref.current.scrollIntoView()
  }, []);

return(

 <>
   <span ref={ref}></span>
   <YourCodeHere />
   <MoreCode />
</>

) }

Upvotes: 8

Andy
Andy

Reputation: 735

Looks like all the useEffect examples dont factor in you might want to trigger this with a state change.

const [aStateVariable, setAStateVariable] = useState(false);

const handleClick = () => {
   setAStateVariable(true);
}

useEffect(() => {
  if(aStateVariable === true) {
    window.scrollTo(0, 0)
  }
}, [aStateVariable])

Upvotes: 5

bellabelle
bellabelle

Reputation: 926

This works for me.

import React, { useEffect } from 'react';

useEffect(() => {
    const body = document.querySelector('#root');

    body.scrollIntoView({
        behavior: 'smooth'
    }, 500)

}, []);

Upvotes: 14

Astrit Spanca
Astrit Spanca

Reputation: 724

I had the same for problem for a while. Adding window.scrollTo(0, 0);to every page is painful and redundant. So i added a HOC which will wrap all my routes and it will stay inside BrowserRouter component:

 <ScrollTop>
    <Routes />
  </ScrollTop>

Inside ScrollTopComponent we have the following:

import React, { useEffect } from "react";
import { useLocation } from "react-router-dom";

const ScrollTop = (props) => {
  const { children } = props;

  const location = useLocation();

  useEffect(() => {
    window.scrollTo(0, 0);
  }, [location]);

  return <main>{children}</main>;
};

export default ScrollTop;

Upvotes: 3

Muhammad Awais
Muhammad Awais

Reputation: 195

The page that appears after clicking, just write into it.

  componentDidMount() {
    window.scrollTo(0, 0);
  } 

Upvotes: 8

react developer
react developer

Reputation: 69

This is what I did:

useEffect(() => ref.current.scrollTo(0, 0));
const ref = useRef()

       return(
         <div ref={ref}>
           ...
         </div>
        )

Upvotes: 4

Buzzcut Season
Buzzcut Season

Reputation: 11

All the solutions talk about adding the scroll on componentDidMount or componentDidUpdate but with the DOM.

I did all of that and didn't worked.

So, figured out some other way that works just fine for me.

Added componentDidUpdate() { window.scrollTo(0, 0) } on the header, that mine is out of the <Switch></Switch> element. Just free in the app. Works.

I also found about some ScrollRestoration thing, but I'm lazy now. And for now going to keep it the "DidUpdate" way.

Upvotes: 0

Mapenzi Mudimba
Mapenzi Mudimba

Reputation: 1

I added an Event listener on the index.html page since it is through which all page loading and reloading is done. Below is the snippet.

// Event listener
addEventListener("load", function () {
    setTimeout(hideURLbar, 0);
}, false);
  
function hideURLbar() {
    window.scrollTo(0, 1);
}

Upvotes: 0

Alexandru Lupu
Alexandru Lupu

Reputation: 311

I tried everything, but this is the only thing that worked.

 useLayoutEffect(() => {
  document.getElementById("someID").scrollTo(0, 0);
 });

Upvotes: 5

Baqer Naqvi
Baqer Naqvi

Reputation: 6514

For Functional components;

import React, {useRef} from 'react';
function ScrollingExample (props) {
// create our ref
const refToTop = useRef();

return (
<h1 ref={refToTop}> I wanna be seen </h1>
// then add enough contents to show scroll on page
<a onClick={()=>{
    setTimeout(() => { refToTop.current.scrollIntoView({ behavior: 'smooth' })}, 500)
        }}>  Take me to the element <a>
);
}

Upvotes: 2

gal007
gal007

Reputation: 7202

Nothing worked for me but:

componentDidMount(){

    $( document ).ready(function() {
        window.scrollTo(0,0);
    });
}

Upvotes: -5

jerryurenaa
jerryurenaa

Reputation: 4712

If all want to do is something simple here is a solution that will work for everybody

add this mini function

scrollTop()
{
    window.scrollTo({
        top: 0,
        behavior: "smooth"
    });
}

call the function as following from the footer of the page

<a className="scroll-to-top rounded" style={{display: "inline"}} onClick={this.scrollTop}>TOP</a>

if you want to add nice styles here is the css

.scroll-to-top {
  position: fixed;
  right: 1rem;
  bottom: 1rem;
  display: none;
  width: 2.75rem;
  height: 2.75rem;
  text-align: center;
  color: #fff;
  background: rgba(90, 92, 105, 0.5);
  line-height: 46px;
}

Upvotes: 9

Awa Dieudone
Awa Dieudone

Reputation: 1

If I assume you are rendering a chapter of say, a book per page, all you need to do is add this to your code. This worked for me like magic.

    componentDidUpdate(prevProps) {
      if (prevProps.currentChapter !== this.props.currentChapter) {
        window.scrollTo(0, 0);
      }
    }

With this, you have no need creating a ref on the component being rendered.

Upvotes: 0

sledgeweight
sledgeweight

Reputation: 8105

Finally.. I used:

componentDidMount() {
  window.scrollTo(0, 0)
}

EDIT: React v16.8+

useEffect(() => {
  window.scrollTo(0, 0)
}, [])

Upvotes: 640

jj0b
jj0b

Reputation: 1126

I ran into this issue building a site with Gatsby whose Link is built on top of Reach Router. It seems odd that this is a modification that has to be made rather than the default behaviour.

Anyway, I tried many of the solutions above and the only one that actually worked for me was:

document.getElementById("WhateverIdYouWantToScrollTo").scrollIntoView()

I put this in a useEffect but you could just as easily put it in componentDidMount or trigger it any other way you wanted to.

Not sure why window.scrollTo(0, 0) wouldn't work for me (and others).

Upvotes: 3

Gabriel Ezenwankwo
Gabriel Ezenwankwo

Reputation: 432

Using Hooks in functional components, assuming the component updates when theres an update in the result props

import React, { useEffect } from 'react';

export const scrollTop = ({result}) => {
  useEffect(() => {
    window.scrollTo(0, 0);
  }, [result])
}

Upvotes: 7

Quentin C
Quentin C

Reputation: 1893

Hook solution:

  • Create a ScrollToTop hook

    import { useEffect } from "react";
    import { withRouter } from "react-router-dom";

    const ScrollToTop = ({ children, location: { pathname } }) => {
      useEffect(() => {
        window.scrollTo({
          top: 0,
          left: 0,
          behavior: "smooth"
        });
      }, [pathname]);

      return children || null;
    };

    export default withRouter(ScrollToTop);

  • Wrap your App with it

    <Router>
        <ScrollToTop>
           <App />
        </ScrollToTop>
    </Router>

Documentation : https://reacttraining.com/react-router/web/guides/scroll-restoration

Upvotes: 32

GavKilbride
GavKilbride

Reputation: 1569

I'm using React Hooks and wanted something re-usable but also something I could call at any time (rather than just after render).

// utils.js
export const useScrollToTop = (initialScrollState = false) => {
  const [scrollToTop, setScrollToTop] = useState(initialScrollState);

  useEffect(() => {
    if (scrollToTop) {
      setScrollToTop(false);
      try {
        window.scroll({
          top: 0,
          left: 0,
          behavior: 'smooth',
        });
      } catch (error) {
        window.scrollTo(0, 0);
      }
    }
  }, [scrollToTop, setScrollToTop]);

  return setScrollToTop;
};

Then to use the hook you can do:

import { useScrollToTop } from 'utils';

const MyPage = (props) => {
  // initialise useScrollToTop with true in order to scroll on page load 
  const setScrollToTop = useScrollToTop(true);

  ...

  return <div onClick={() => setScrollToTop(true)}>click me to scroll to top</div>
}

Upvotes: 3

Powderham
Powderham

Reputation: 1640

For those using hooks, the following code will work.

React.useEffect(() => {
  window.scrollTo(0, 0);
}, []);

Note, you can also import useEffect directly: import { useEffect } from 'react'

Upvotes: 23

J. Mark Stevens
J. Mark Stevens

Reputation: 4945

You could use something like this. ReactDom is for react.14. Just React otherwise.

    componentDidUpdate = () => { ReactDom.findDOMNode(this).scrollIntoView(); }

Update 5/11/2019 for React 16+

  constructor(props) {
    super(props)
    this.childDiv = React.createRef()
  }

  componentDidMount = () => this.handleScroll()

  componentDidUpdate = () => this.handleScroll()

  handleScroll = () => {
    const { index, selected } = this.props
    if (index === selected) {
      setTimeout(() => {
        this.childDiv.current.scrollIntoView({ behavior: 'smooth' })
      }, 500)
    }
  }

Upvotes: 62

Todd
Todd

Reputation: 581

None of the above answers is currently working for me. It turns out that .scrollTo is not as widely compatible as .scrollIntoView.

In our App.js, in componentWillMount() we added

this.props.history.listen((location, action) => {
        setTimeout(() => { document.getElementById('root').scrollIntoView({ behavior: "smooth" }) }, 777)
    })

This is the only solution that is working universally for us. root is the ID of our App. The "smooth" behavior doesn't work on every browser / device. The 777 timeout is a bit conservative, but we load a lot of data on every page, so through testing this was necessary. A shorter 237 might work for most applications.

Upvotes: 2

Todd
Todd

Reputation: 581

None of the above answers is currently working for me. It turns out that .scrollTo is not as widely compatible as .scrollIntoView.

In our App.js, in componentWillMount() we added

    this.props.history.listen((location, action) => {
            setTimeout(() => { document.getElementById('root').scrollIntoView({ behavior: "smooth" }) }, 777)
        })

This is the only solution that is working universally for us. root is the ID of our App. The "smooth" behavior doesn't work on every browser / device. The 777 timeout is a bit conservative, but we load a lot of data on every page, so through testing this was necessary. A shorter 237 might work for most applications.

Upvotes: 0

Matt Carlotta
Matt Carlotta

Reputation: 19772

Here's yet another approach that allows you to choose which mounted components you want the window scroll position to reset to without mass duplicating the ComponentDidUpdate/ComponentDidMount.

The example below is wrapping the Blog component with ScrollIntoView(), so that if the route changes when the Blog component is mounted, then the HOC's ComponentDidUpdate will update the window scroll position.

You can just as easily wrap it over the entire app, so that on any route change, it'll trigger a window reset.

ScrollIntoView.js

import React, { Component } from 'react';
import { withRouter } from 'react-router';

export default WrappedComponent => {
  class ResetWindowScroll extends Component {
    componentDidUpdate = (prevProps) => {
      if(this.props.location !== prevProps.location) window.scrollTo(0,0);
    }

    render = () => <WrappedComponent {...this.props} />
  }
  return withRouter(ResetWindowScroll);
}

Routes.js

import React from 'react';
import { Route, IndexRoute } from 'react-router';

import App from '../components/App';
import About from '../components/pages/About';
import Blog from '../components/pages/Blog'
import Index from '../components/Landing';
import NotFound from '../components/navigation/NotFound';
import ScrollIntoView from '../components/navigation/ScrollIntoView';

 export default (
    <Route path="/" component={App}>
        <IndexRoute component={Index} />
        <Route path="/about" component={About} /> 
        <Route path="/blog" component={ScrollIntoView(Blog)} />
        <Route path="*" component={NotFound} />
    </Route>
);

The above example works great, but if you've migrated to react-router-dom, then you can simplify the above by creating a HOC that wraps the component.

Once again, you could also just as easily wrap it over your routes (just change componentDidMount method to the componentDidUpdate method example code written above, as well as wrapping ScrollIntoView with withRouter).

containers/ScrollIntoView.js

import { PureComponent, Fragment } from "react";

class ScrollIntoView extends PureComponent {
  componentDidMount = () => window.scrollTo(0, 0);

  render = () => this.props.children
}

export default ScrollIntoView;

components/Home.js

import React from "react";
import ScrollIntoView from "../containers/ScrollIntoView";

export default () => (
  <ScrollIntoView>
    <div className="container">
      <p>
        Sample Text
      </p>
    </div>
  </ScrollIntoView>
);

Upvotes: 10

Related Questions