Jack
Jack

Reputation: 448

How to fix Gatsby JS Link component retaining scroll position and not resetting to top

I'm setting up a website using Gatsby 2.2.10 and the Link components are retaining the scroll positions of the previous page and not scrolling back to the top when they're clicked.

<div className="Footer__legal body">
 <p>© {new Date().getFullYear()} My Nice Company</p>
 <Link to="/privacy-policy">Privacy Policy</Link>
 <Link to="/page-2">Page 2 Link component</Link>
</div>

Expected behaviour:

When you click 'Privacy Policy', 'Page 2' or any page at the bottom of the website, I expect the page to load with user being back at the top.

Actual Behaviour:

User stays at scroll position of the current page

Upvotes: 22

Views: 15484

Answers (12)

David Beauchamp
David Beauchamp

Reputation: 250

This seems to be a bug in Gatsby when css html { scroll-behavior: smooth; } is used in conjunction with a sticky element. Gatsby contributor in Github says they should have this patched soon. In that same PR comment he offers a workaround. Ive added a bit more to this workaround to allow jump links like www.yoursite.com/#scroll-down-to-important-info

// This would go in gatsby-browser.js

exports.shouldUpdateScroll = ({ routerProps: { location }, getSavedScrollPosition }) => {
    const currentPosition = getSavedScrollPosition(location);
    if (!location.hash){
        setTimeout(() => {
            window.scrollTo({ top: (currentPosition[0] || 0), left: (currentPosition[1] || 0), behavior: 'instant' });
        }, 0);
        return false;
    }
};

Upvotes: 2

Ajdin Bečirović
Ajdin Bečirović

Reputation: 104

In my case the problem was that I had set scroll-behavior: smooth, on the html element like this:

html {
  scroll-behavior: smooth;
}

After removing this property new pages would be opened without any scroll.

Upvotes: 3

Folyik
Folyik

Reputation: 183

To further some of the answers above, I had to stick a time out in the useEffect to get it to work consistently. Seems to work with a timeout as little as 1ms.

useEffect(() => {
  let windowScrollTimeout = setTimeout(() => {
    window.scrollTo(0, 0)
    clearTimeout(windowScrollTimeout)
  }, 1)
}, [])

Upvotes: 0

loudo
loudo

Reputation: 53

I have a header set with position: fixed; and then a div with margin-top: 100px; so my content starts below the header (that has a 100px height). I had the scrolling issue until I changed margin to padding: padding-top: 100px;

Upvotes: 1

Sam
Sam

Reputation: 2341

If your page is a functional component, you can use the useEffect hook to scroll back to the top of the page, assuming that you are using graphql and so your component takes some data as an argument. This way every time that data changes, you scroll to the top of the page(works similar to componentDidUpdate).

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

Upvotes: 5

F&#233;lix Paradis
F&#233;lix Paradis

Reputation: 6051

In my CSS, I had

html, body {
  height: 100%;
  overflow-y: scroll;
}

because it was the only way to fix a mobile-safari-only bug (my favorites!)

If you also can't get rid of overflow properties, I suggest this:

// in gatsby-browser.js
export function shouldUpdateScroll(prevRouterProps, { location }) {
  window.scrollTo(0, 0)
  const body = document.getElementsByTagName('body')[0]
  body.scrollTop = 0
  return false
}

Upvotes: 1

Jay
Jay

Reputation: 358

My problem was that I had html {height: 100%}. Removing that rule seemed to have solved the problem.

I also have gatsby-plugin-transition-link in my project, which has added more wrappers around my main content.

Upvotes: 1

KMA Badshah
KMA Badshah

Reputation: 1125

useEffect(() => window.scrollTo(0, 0), []) solved the issue for me. I was having the problem only on firefox but not on chrome. Idk what was causing the issue but it works fine now.

Upvotes: 4

Tiago Gimenes
Tiago Gimenes

Reputation: 21

Ok, this is an old question, but I've recently faced the same odd behavior. Turns out my problem was different from all other solutions I've seen in the internet so far.

Gatsby uses a package called gatsby-react-router-scroll. This package manages Gatsby's scroll behavior. This behavior is mainly implemented in Scroll Handler component. This component only scrolls up in componentDidUpdate method, and not in the componentDidMount one. I've added a debugger in the componentDidMount method and ScrollHandler component was being re-mounted at each navigation.

The fix was to figure out why this component was being mounted and remounted at each navigation. In my case, I had a React.StrictMode component on wrapRootElement function in my gatsby-browser.js. Removing StrictMode made ScrollHandler component stable, thus solving my problem.

Upvotes: 2

Addison
Addison

Reputation: 169

If you have overflow: hidden or overflow: auto set on body, you'll have this issue!

Upvotes: 11

Jack
Jack

Reputation: 448

Figured out a workaround by converting index.js page into a class-based component and then added

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

Not the cleanest fix nor do I know why it wasn't scrolling automatically, going to a JS meet up next week so will ask the question then and post a follow up if I get an answer.

I have a feeling it's something to do with my styles, as I started a new project and the Gatsby-cli had no issues. Will be refactoring styles to see if this fixes the issue.

Note: Returning to this following Michael's answer, it also related to an overflow: hidden; style I had on the body, removing this also fixed my issue.

Upvotes: 1

Michael W. Czechowski
Michael W. Czechowski

Reputation: 3455

You can also modify gatsby-browser.js and implement a hook for each scroll update:

// in gastby-browser.js
exports.shouldUpdateScroll = ({
  routerProps: { location },
  getSavedScrollPosition,
}) => {
  const { pathname } = location
  // list of routes for the scroll-to-top-hook
  const scrollToTopRoutes = [`/privacy-policy`, `/page-2`]
  // if the new route is part of the list above, scroll to top (0, 0)
  if (scrollToTopRoutes.indexOf(pathname) !== -1) {
    window.scrollTo(0, 0)
  }

  return false
}

You will find the code for shouldUpdateScroll on GitHub or in the documentation for shouldUpdateScroll on the GatsbyJS website.

Upvotes: 9

Related Questions