Scrolling to top on page changes in React Router (v5)

Published on in JavaScript and React

A common solution is to scroll to top every time the page changes. That's incorrect: the page should be scrolled to top only when navigating to new pages, not when navigating back or forward.

Table of contents

Common but wrong solution

When navigating in a Single Page Application (SPA), you have to handle scrolling manually. Here's a solution that's often recommended, but this is wrong:

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

// Use this hook in some top-level component
// which is inside a `<Router>` component
function useOnPageChange() {
  const { pathname } = useLocation()

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

It's wrong because the page is scrolled to top on all page changes – even when using the browser's back and forward navigation buttons.

Compare with any non-SPA site, like my website or Wikipedia. When using the browser's back/forward buttons, the previous/next page's scrolling position should be restored. The page should be scrolled to top only when navigating to new pages.

Correct solution

The fix is to check the value of the history object's action parameter:

import { useHistory, useLocation } from 'react-router-dom'

function useOnPageChange() {
  const { action } = useHistory()
  const { pathname } = useLocation()

  useEffect(() => {
    // Do nothing when using the browser's back/forward buttons
    if (action === 'POP') return

    window.scrollTo(0, 0)
  }, [action, pathname])
}

You could (and probably should) also do other things in the hook, like focus the main heading of the new page.