gatsby: Internal anchors with hashes not working

Description

Internal anchors with hashes stop working after the first click. See the minimal test case repo at https://github.com/ediblecode/gatsby-anchors-issue and demo site at https://frosty-brown-de3460.netlify.app/. Click into page 2 and click the various internal anchors on that page.

Note: this is using simple anchor tags as per the docs for gatsby link:

Neither <Link> nor navigate can be used for in-route navigation with a hash or query parameter. If you need this behavior, you should either use an anchor tag…

I thought this was fixed by #25749 and released in 2.24.3 but it still seems to be an issue. I think it’s related to, if not the same as, #25554, #25745 and #25522. I wonder if it was caused by #24306 (or at least related in some way).

The first anchor seems to be OK but it’s definitely something to do with subsquent clicks. Weirdly though it’s non-determinate: sometimes it will work fine and sometimes it won’t and I can’t seem to work out the pattern. Looking into it, session storage is used under the hood for the scroll position, so I wonder if it’s something to do with that - maybe the key used isn’t including the anchor hash or something? Anyway, that’s as far as I got with my digging, sorry. Hoping @blainekasten you might be able to take a look as you’ve looked at the other related issues. Thank you very much.

Steps to reproduce

  1. Clone the gatsby starter
  2. Upgrade dependencies to latest via npx npm-check-updates -u
    1. This includes Gatsby 2.24.3
  3. Run npm i to install these dependencies
  4. Create a page with hash links to elements on a page with matching ids (see example repo and site)
  5. Click the internal hash anchors to try to jump up and down the page.

Expected result

The URL is updated with the hash, and the page is scrolled to the element with an id matching the hash in the URL.

Actual result

The first click scrolls to the target element and updates the URL but subsequent clicks only update the URL without moving down the page.

Environment

System: OS: Windows 10 10.0.19041 CPU: (4) x64 Intel® Core™ i7-5500U CPU @ 2.40GHz Binaries: Node: 12.16.3 - C:\Program Files\nodejs\node.EXE Yarn: 1.16.0 - C:\Program Files (x86)\Yarn\bin\yarn.CMD npm: 6.14.4 - C:\Program Files\nodejs\npm.CMD Languages: Python: 2.7.11 - C:\Python27\python.EXE Browsers: Edge: 44.19041.1.0 npmPackages: gatsby: ^2.24.3 => 2.24.3 gatsby-image: ^2.4.13 => 2.4.13 gatsby-plugin-manifest: ^2.4.18 => 2.4.18 gatsby-plugin-offline: ^3.2.18 => 3.2.18 gatsby-plugin-react-helmet: ^3.3.10 => 3.3.10 gatsby-plugin-sharp: ^2.6.19 => 2.6.19 gatsby-source-filesystem: ^2.3.19 => 2.3.19 gatsby-transformer-sharp: ^2.5.11 => 2.5.11

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 16
  • Comments: 28 (12 by maintainers)

Commits related to this issue

Most upvoted comments

A PR with the fix for this based on @lukekarrys suggestion above is open now: #28555

I found a very nice workaround. No need to keep older versions or use any effect hooks.

Write the following lines of code in the file gatsby-browser.js:

export const shouldUpdateScroll = ({ routerProps: { location } }) => {
  if (location.hash === '') {
    window.scrollTo(0, 0);
  }

  return false;
};

This seems like too simple of a fix for it to be right, but does this work for anyone else? I noticed that in the gatsby-react-router-scroll/scroll-handler it was checking for a scroll position of 0 in order to scroll to the hash node. I confirmed on my site that if I was scrolled to the top of the window it worked, but if I was scrolled down at all it didn’t.

So I experimented by running patch-package with the patch below and it worked for me.

diff --git a/node_modules/gatsby-react-router-scroll/scroll-handler.js b/node_modules/gatsby-react-router-scroll/scroll-handler.js
index 91114f2..9284f09 100644
--- a/node_modules/gatsby-react-router-scroll/scroll-handler.js
+++ b/node_modules/gatsby-react-router-scroll/scroll-handler.js
@@ -104,7 +104,7 @@ var ScrollHandler = /*#__PURE__*/function (_React$Component) {
       scrollPosition = this._stateStorage.read(this.props.location, key);
     }
 
-    if (hash && scrollPosition === 0) {
+    if (hash) {
       this.scrollToHash(decodeURI(hash), prevProps);
     } else {
       this.windowScroll(scrollPosition, prevProps);

Hold your horses Gatsby bot - I don’t think this is fixed and patch-package is a hack/workaround. Not stale!

What is the verdict on this? I am concerned it will affect SEO on a large number of my clients pages if the links are not connecting properly.

It looks like this is happening again on Gatsby v5. I noticed the behavior appearing after migrating this week from v4.

I am also having this issue.

+1

@lukekarrys patch seems to be working well for me though.

I should point out as well that the hacky workaround isn’t ideal because it doesn’t move focus to the correct place in the document when you load a page that includes a hash. So keyboard users who try to tab from the id matching the hash can’t and they start back at the top of the document. So not great for accessibility. So use that workaround with caution until it’s fixed. (this is because it missed out on all the goodness from #24306)

I think I have a workaround that seems to be OK (if hacky). There were various comments on #25554 about downgrading gatsby (e.g. to 2.24.18) or downgrading gatsby-react-router-scroll to 3.0.3. Downgrading gatsby seemed to introduce other issues, for example I came across #20082 and I wasn’t a fan of downgrading the whole of gatsby just to fix this issue. So I opted for the specific version of gatsby-react-router-scroll, like the comments.

I checked our existing package-lock.json and it was using gatsby-react-router-scroll@3.0.0, so I went with that version to be safe, although people do seem to suggest 3.0.3 would be OK. (I haven’t actually reviewed which version the issue was introduced in).

Unfortunately, the idea of using resolutions in package.json, although great if you’re using yarn, didn’t work for us as we’re using npm, which doesn’t support resolutions natively (I wonder if that’s the reason for this comment). So instead I used the npm-force-resolutions package to replicate this yarn functionality. So my package.json looks like this:

{
  "scripts": {
+    "preinstall": "npx npm-force-resolutions",
  },
"devDependencies": {
  "gatsby": "2.24.3",
+  "gatsby-react-router-scroll": "3.0.0",
},
+"resolutions": {
+    "gatsby-react-router-scroll": "3.0.0"
+  }

Notice I needed gatsby-react-router-scroll in both devdeps and resolutions to avoid getting “Can’t resolve ‘gatsby-react-router-scroll’ in ‘.cache’” errors, even though I’m not using it as a dependency directly.

To be honest, it’s a bit hacky but it does mean it works as I was being blocked from any new releases because of the functional regression and the trouble I was having downgrading. I can easily back out the line in the diff above when the issue is fixed.

@josephmarkus maybe this could fix your issue?

@edysmp it doesn’t fix it for me unfortunately… 😦

Still not resolved AFAIK actions bot

@dalxds not used themes, but probably makes sense to be in the app’s I think