gatsbyjs / gatsby

The best React-based framework with performance, scalability and security built in.
https://www.gatsbyjs.com
MIT License
55.28k stars 10.31k forks source link

Gatsby does not scroll to the top on route change after upgrading to v5 #38201

Open dslovinsky opened 1 year ago

dslovinsky commented 1 year ago

Preliminary Checks

Description

When the user navigates to a new page using Gatsby Link, the page does not scroll the user to the top. This often means the user is left at the bottom of a page or somewhere in the middle despite never having navigated to the page before. This only started happening after upgrading to Gatsby v5.

A version of the bug can be seen on Calendly's website in this Loom video.

Possible explanation

The bug seems to occur when DOM elements are added/loaded on the page during rehydration. With scroll-behavior: smooth; the browser begins to move the window to the top of the page, but is interrupted by DOM elements changing the page position, causing it to stop the animation. Notably, the issue does not occur when scroll-behavior is auto.

In the minimal reproduction I have recreated the issue using Marketo forms to load DOM elements, since I noticed pages with Marketo forms seem to most reliably have the issue.

Similar issues

This one seems similar but was closed out after a fix was implemented on an older Gatsby version.

Reproduction Link

https://codesandbox.io/p/sandbox/gatsby-starter-minimal-sr3hof

Steps to Reproduce

In the minimal reproduction:

  1. Start the dev server yarn run start

  2. Open the homepage at http://localhost:8001/

  3. Scroll to the bottom of the page and click the "To Test A" link. Clicking the bottom link to the homepage should also trigger the bug.

Expected Result

The user should be scrolled to the top of the page after navigating to it for the first time via a Gatsby Link.

Actual Result

The user is left at or near the bottom of the page.

Environment

System:
  OS: macOS 11.6
  CPU: (8) x64 Intel(R) Core(TM) i5-1038NG7 CPU @ 2.00GHz
  Shell: 5.8 - /bin/zsh
Binaries:
  Node: 18.13.0 - ~/.nvm/versions/node/v18.13.0/bin/node
  Yarn: 1.22.19 - ~/.nvm/versions/node/v18.13.0/bin/yarn
  npm: 8.19.3 - ~/.nvm/versions/node/v18.13.0/bin/npm
Languages:
  Python: 2.7.16 - /usr/bin/python
Browsers:
  Chrome: 113.0.5672.126
  Firefox: 111.0.1
  Safari: 14.1.2
npmPackages:
  gatsby: ^5.9.1 => 5.10.0

Config Flags

No response

nicolawebdev commented 1 year ago

+1 same issue on v5

arsinclair commented 1 year ago

This is happening for us as well. I noticed that the amount of scroll offset on page navigation exactly correlates with the height of a fixed-position navbar that we have on every page. The both auto and smooth values of scroll-behavior don't seem to have any effect.

In our use case there's a hero image on every page and because of this issue basically a part of header image is getting scrolled away and is invisible. This essentially prevents us from upgrading from Gatsby v4 to v5, since it's a bad UX.

It's been a month after this issue was raised, can someone from Gatsby team take a look at this? What are the next steps?

arsinclair commented 1 year ago

Turns out, it's exactly the same issue as in #37719. The navbar in our implementation has a top margin, and it scrolls down for the amount of that margin. Replacing the margin with padding solves the issue. We ended up rewriting the navbar to not use the top margin at all and use a position: sticky approach.

It would be interesting to know why this behaviour changed in Gatsby 5 (may be related to webpack upgrades?), but our issue is solved.

dslovinsky commented 1 year ago

Turns out, it's exactly the same issue as in #37719. The navbar in our implementation has a top margin, and it scrolls down for the amount of that margin.

I was a bit skeptical at first but you seem to be right: removing the 8px margin on the body element from the user agent stylesheet in the minimal reproduction fixes the scroll issue. Very strange behavior.

dslovinsky commented 1 year ago

Mentioned in the Loom video above, but worth sharing separately here: This is a workaround solution I've used while the issue is addressed in Gatsby:

// gatsby-browser.tsx

export const shouldUpdateScroll: GatsbyBrowser['shouldUpdateScroll'] = ({
    routerProps: { location },
    getSavedScrollPosition,
}) => {
    const currentPosition = getSavedScrollPosition(location);

    setTimeout(() => {
        window.scrollTo(...(currentPosition || [0, 0]));
    }, 0);

    return false;
};

The key is putting the window.scrollTo call in a setTimeout. The delay doesn't matter - even 0ms works. The point is to move it to later in the event loop, so that it won't be interrupted.

sanjastojkova commented 1 year ago

+1

I recently updated my project from Gatsby 2 to Gatsby 5, and I've been facing some scroll-related problems. Before the update, the initial scroll position would be stored when a user first accessed the page. However, after the update, it seems like the initial scroll position is not being stored properly when a user first access the page, then it is working fine.

To give you an example, when I visit the page "kinto-share/om-kinto-mobility" -> /kinto-share/kom-igang/ and then go back, it redirects me to the top of the page instead of the [0, 4440] position where it should be(Why @@scroll|/kinto-share/om-kinto-mobility/|1690791782785 is not. being stored as |initial ?). This behavior was working perfectly before the update.

I have included the following code snippet in my gatsby-browser.tsx file to handle scroll restoration:

export const shouldUpdateScroll = ({ routerProps: { location }, getSavedScrollPosition }) => {
    window.history.scrollRestoration = 'manual';
    const currentPosition = getSavedScrollPosition(location, location.key);
    if (!currentPosition) {
        window.scrollTo(0, 0);
    } else {
        window.setTimeout(() => {
            window.requestAnimationFrame(() => {
                window.scrollTo(...currentPosition);
            });
        }, 0);
    }

    return false;
};

I would greatly appreciate any insights or suggestions on how to resolve this issue and properly restore the initial scroll position when users access the page. Thank you in advance for your help! gatsby 5 image

gatsby 2 image

rogerf76 commented 2 months ago

Any update on this?

I am seeing this bug - Only on Chrome, on my iPhone.

For a certain build it will happen consistently when clicking from Page A to Page B. After navigation Page B is scrolled to the bottom.

Next build it will no longer happen when clicking from Page A to Page B, but may happen from Page C to Page E, or may not happen at all.

I don't think this is the same as issue #37719. Yes, I have a margin at the top of my page for a nav bar. However, #37719 reports the issue as showing on Chrome desktop. I have not seen this happen on Chrome desktop, I have only seen it on Chrome mobile.

edwardoverthere commented 3 weeks ago

Chiming in here - also seeing this issue on a client project I am working on.

Oddly enough, if I remove smooth scrolling from our html styling - we dont have this issue. However, I don't want to "fix" it this way as this results in a visually jarring experience for our users when they click anchor links.