Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NEXT-658] Nextjs 13 Link not scrolling to anchor element #44295

Closed
1 task done
vladgardus opened this issue Dec 23, 2022 · 43 comments · Fixed by #46995
Closed
1 task done

[NEXT-658] Nextjs 13 Link not scrolling to anchor element #44295

vladgardus opened this issue Dec 23, 2022 · 43 comments · Fixed by #46995
Labels
bug Issue was opened via the bug report template. linear: next Confirmed issue that is tracked by the Next.js team.

Comments

@vladgardus
Copy link

vladgardus commented Dec 23, 2022

Verify canary release

  • I verified that the issue exists in the latest Next.js canary release

Provide environment information

Operating System:
  Platform: win32
  Arch: x64
  Version: Windows 10 Pro
Binaries:
  Node: 16.13.1
  npm: N/A
  Yarn: N/A
  pnpm: N/A
Relevant packages:
  next: 13.0.6
  eslint-config-next: N/A
  react: 18.2.0
  react-dom: 18.2.0

Which area(s) of Next.js are affected? (leave empty if unsure)

No response

Link to the code that reproduces this issue

no github link

To Reproduce

  • Add Link with href={{ pathname: /, hash: "home" }}
  • add div with id="home"
  • clicking on link will not scroll to div

Describe the Bug

Working on a next 13 app using the experimental flag.

Created a header that has 3 next/link elements and the purpose of those 3 links is to set a hash on the URL and scroll to that anchor element.

The issue is that clicking on those links change the URL but does not scroll to the anchor element, but the strange thing is that by manually changing the hash in the URL does in fact trigger that scroll to the anchor element.

Expected Behavior

Links that add hashes to URL should scroll to the anchor element.

Which browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

No response

NEXT-658

@vladgardus vladgardus added the bug Issue was opened via the bug report template. label Dec 23, 2022
@apriltaoyvr
Copy link

Can confirm that this is happening on 13.1.1

@myl7
Copy link

myl7 commented Dec 25, 2022

Can use plain old <a> to temporarily resolve it, in case anyone like me gets stuck by this

@pabx12
Copy link

pabx12 commented Feb 22, 2023

i have the same problem

@magnusriga
Copy link

magnusriga commented Feb 23, 2023

Same issue here. Neither /foo/#bar nor /foo#bar works.

@olavocarvalho
Copy link

Im facing the same issue. Workaround: currently using the react-scroll lib to scroll between sections in the page.

@magnusriga
Copy link

magnusriga commented Feb 23, 2023

I solved this by checking, in a component with an id that might be scrolled to, whether it could find an element with the id set in window.location.hash. If so, scroll to it. I created the below hook to make it easier to use.

PS: The first component that uses the hook and finds the element, will handle the scroll. I tried to target the specific current element with a ref callback, but it did not scroll every time (have no idea why, see suggestion below).

Hook that always works:

import { useEffect, useContext } from "react";
import { NavbarContext } from "@context/NavbarContext";

const useSmoothScrollTo = (targetHash = null) => {
  const { navRect } = useContext(NavbarContext);

  useEffect(() => {
    if (typeof window === "undefined") return;
    const hash = targetHash || window.location.hash;

    if (!hash) return;

    const headerOffset = navRect?.height || "70";

    // Only proceed if target is a single hash tag
    const startWithHashRegex = /^#\w+/g;
    if (!startWithHashRegex.test(hash)) return;

    let target = document.querySelectorAll(`${hash}`)[0];

    if (!target) return;

    const elementPosition = target.getBoundingClientRect().top;
    const offsetPosition = elementPosition + window.pageYOffset - headerOffset;

    window.scrollTo({
      top: offsetPosition,
      behavior: "smooth",
    });
  }, [navRect.height, targetHash]);
};

export default useSmoothScrollTo;

Callback ref hook that only scrolls now and then:

import { useContext, useCallback } from "react";
import { NavbarContext } from "@context/NavbarContext";

const useSmoothScrollTo = (hash = null) => {
  const { navRect } = useContext(NavbarContext);

  const ref = useCallback(
    (node) => {
      if (node !== null) {
        // Runs on mount of node

        if (typeof window === "undefined") return;
        const targetHash = hash || window.location.hash;

        if (!targetHash) return;

        // Only proceed if target is a single hash tag
        const startWithHashRegex = /^#\w+/g;
        if (!startWithHashRegex.test(targetHash)) return;

        const targetHashId = targetHash.substring(1);

        const headerOffset = navRect?.height || "70";

        const currentNodeId = node.id;

        if (targetHashId !== currentNodeId) return;

        const elementPosition = node.getBoundingClientRect().top;
        const offsetPosition =
          elementPosition + window.pageYOffset - headerOffset;

        window.scrollTo({
          top: offsetPosition,
          behavior: "smooth",
        });
      } else {
        // Runs on unmount of node
      }
    },
    [navRect.height, hash]
  );

  return ref;
};

export default useSmoothScrollTo;

@grzegorzpokorski
Copy link

Another strange behavior which I am noticing in nextjs v13.2 with experimantal /app dir is that when I have a link e.g.: <Link href="#place-on-page">link</Link> on page located on /page path, it always redirect to /#place-on-page instead /page#place-on-page.

@nosovandriy
Copy link

Next 13.2.1 the same trouble with the anchor link

@timneutkens timneutkens added the linear: next Confirmed issue that is tracked by the Next.js team. label Feb 27, 2023
@timneutkens timneutkens changed the title Nextjs 13 Link not scrolling to anchor element [NEXT-658] Nextjs 13 Link not scrolling to anchor element Feb 27, 2023
@jayantasamaddar
Copy link

jayantasamaddar commented Mar 6, 2023

Facing this same issue in Next 13.2.3

Can use plain old <a> to temporarily resolve it, in case anyone like me gets stuck by this

@myl7 That will come with the window scroll-to-top problem, which has to be resolved on its own.

@magnusriga The issue isn't that we cannot write an useEffect to handle this. The issue is: "Really, that's how we are gonna do things?" so although calculating the offSet and manually scrolling may work, that's not the route we want to take at all. Not optimal.

The earlier docs had a section showing an example with hash links and disabling scroll to top with as seen HERE

<Link href={url} scroll={false}>Click Me</Link>

The current docs don't even mention it.

@grzegorzpokorski
Copy link

URL hash (#) handling is in the Routing roadmap (https://beta.nextjs.org/docs/app-directory-roadmap), so we just need to wait for it to arrive.

timneutkens added a commit that referenced this issue Mar 14, 2023
Adds support for scrolling based on the [hash
fragment](https://en.wikipedia.org/wiki/URI_fragment) in client-side
navigations for the App Router, mirroring browser behavior.

- `#main-content` → scrolls to `id="main-content"` or
`name="main-content"` property
- `#top` → scrolls to the top of the page, this is a special case in
browsers.
- no hash → default scroll behavior, layout that changed

Fixes NEXT-658

<!-- Thanks for opening a PR! Your contribution is much appreciated.
To make sure your PR is handled as smoothly as possible we request that
you follow the checklist sections below.
Choose the right checklist for the change(s) that you're making:

## For Contributors

### Improving Documentation or adding/fixing Examples

- The "examples guidelines" are followed from our contributing doc
https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md
- Make sure the linting passes by running `pnpm build && pnpm lint`. See
https://github.com/vercel/next.js/blob/canary/contributing/repository/linting.md

### Fixing a bug

- Related issues linked using `fixes #number`
- Tests added. See:
https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs
- Errors have a helpful link attached, see
https://github.com/vercel/next.js/blob/canary/contributing.md

### Adding a feature

- Implements an existing feature request or RFC. Make sure the feature
request has been accepted for implementation before opening a PR. (A
discussion must be opened, see
https://github.com/vercel/next.js/discussions/new?category=ideas)
- Related issues/discussions are linked using `fixes #number`
- e2e tests added
(https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs
- Documentation added
- Telemetry added. In case of a feature if it's used or not.
- Errors have a helpful link attached, see
https://github.com/vercel/next.js/blob/canary/contributing.md



## For Maintainers

- Minimal description (aim for explaining to someone not on the team to
understand the PR)
- When linking to a Slack thread, you might want to share details of the
conclusion
- Link both the Linear (Fixes NEXT-xxx) and the GitHub issues
- Add review comments if necessary to explain to the reviewer the logic
behind a change

### What?

### Why?

### How?

Closes NEXT-
Fixes #

-->

fixes #44295

---------

Co-authored-by: JJ Kasper <jj@jjsweb.site>
@katakeynii
Copy link

Having the same trouble in Next 13.2.4 while using Link getting back to a tag

@franknoel
Copy link

I also have the same issue on 13.2.4, it worked before.

@pdhoward
Copy link

Same issue with 13.3.0

@tomas-cani
Copy link

Same issue with 13.3.2 when using router.push

@magnusriga
Copy link

magnusriga commented Apr 30, 2023

I wanted to point out that linking to hash fragments now work in Next (as of a recent update), however it does not seem it is possible to make it scroll instead of jumping straight to the location. I also couldn't find a built-in way of accounting for the navbar height, so the current implementation by next.js is suboptimal.

I have implemented a custom solution using scrollTo, for now, but looking forward to a built-in solution.

PS: The final solution would ideally scroll to the element, if opting in, both when on the same page as the target anchor, and when coming from another page.

@vanyapr
Copy link

vanyapr commented May 3, 2023

Next js perceives the anchor link as a link to the main page. Now I have fixed it as follows, but scrolling the page to the anchor does not work.

<Link href={${document?.location.href}${link}}>{title}</Link>

@vanyapr
Copy link

vanyapr commented May 3, 2023

Here is my solution. First of all, I implemented a hook based on the suggestion of @magnusriga

const useScrollToAnchor = (offset: number = 70) => {
  return (targetAnchor: string) => {
    console.log('Вызван хук');
    if (!(typeof window === 'undefined')) {
      const hash = targetAnchor;

      const startWithHashRegex = /^#\w+/g;
      const targetElement = document?.querySelector(`${hash}`);

      if (!startWithHashRegex.test(hash) || !targetElement) {
        return;
      }

      const elementPosition = targetElement.getBoundingClientRect().top;
      const offsetPosition = elementPosition + window.pageYOffset - offset;

      const scroll = () => {
        window.scrollTo({
          top: offsetPosition,
          behavior: 'smooth',
        });
      };

      requestAnimationFrame(scroll);
    }
  };
};

export default useScrollToAnchor;

Next, I use this hook in the component. I replaced the links to the buttons, because the idea allows it.

import useScrollToAnchor from '@hooks/useScrollToAnchor';

function Component() {
	const scrollToAnchor = useScrollToAnchor(42);

	const makeActive = (link: string) => {
	  scrollToAnchor(link);
	};

	return (
		<button onClick={() => makeActive(link)} onKeyDown={() => makeActive(link)}>
		  {title}
		</button>
	)
}

In my solution, I am disconnected from the window object or the routing of the application, respectively, it is guaranteed to work. I hope it will help someone.

@SaveliiLukash
Copy link

SaveliiLukash commented May 16, 2023

Next 13.4.2 using app directory. I have a Link with href="#all" property on the /transactions/deposits page.
As far as browser knows the resulting destination is /transactions/deposits#all, however I get routed to the homepage / when following the link.

Works as expected with native a anchors

@Maxservais
Copy link

I still face the issue as well - it links to the main page instead of the anchor element. You can try it live here: https://web3xplorer.com/

@hannupekka
Copy link

Same issue here.

@francisceril
Copy link

Having the same issue but only to the published app on Azure. On my local dev environment, it works as expected.

@eme-hache
Copy link

Having the same issue, but is funny, sometimes it works.

@AlexEnrique
Copy link

Same issue in NextJS v13.4.1. Using the default anchor as a temporary solution.

@ShortyLogos
Copy link

ShortyLogos commented May 25, 2023

I have the same problem. With a component, I'm trying to direct the user to "/anotherpage#dynamicanchor" from home page ("/") and it does so with a really strange offset. The temporary fix that works for me is using a traditional anchor HTML element but it obviously makes the page reloads, which is not ideal.

@oliverkidd
Copy link

Just to confirm. Is there seriously still not a functional way to build anchor links to elements on the same page in a supposedly "stable" release of nextJs? The only workaround I can find using the Link element is to pass in the full URL by obtaining the current pathname from usePathName() and appending the anchor link. Surely there is a better way?

@ShortyLogos
Copy link

The fix I talked about (using an HTML anchor element) works in dev environment but not in production. Any idea why?

@BeldimanAR
Copy link

I found a weird fix I guess. Btw I still have the problem in the latest version on Next.js... So I put on the container which holds all my components in the page.tsx in the app dir overflow-y: auto; .. Apparently this css property makes Link not scrolling. If i comment out this line it works after.. I tried to play with it to see what breaks it but I still couldnt find a solution. Maybe someone else find one

@danlevison
Copy link

danlevison commented May 31, 2023

I am having the same issue. In dev on my local server, scroll to hash-id works. However, once deployed on Netlify it does not work. If I inspect the 'Link' tags ('a' tags) I can click the /#id and it will take me to the correct section of my page.

Also, I cannot seem to get smooth-scrolling to work even on my local server, it just jumps to the /#id. I hope we can find a fix for all this.

@rutenisrailanfq
Copy link

can confirm that these issues are present on:

"next": "13.4.4",

@mtschuden
Copy link

I had the same problem.
I solved the problem by removing overflow: scroll; in my parent component.

@magnusriga
Copy link

I had the same problem. I solved the problem by removing overflow: scroll; in my parent component.

Does it automatically smooth scroll to fragments (hash ids)? Both when coming from another page and for in-page links?

@mtschuden
Copy link

Does it automatically smooth scroll to fragments (hash ids)? Both when coming from another page and for in-page links?

Yes it scrolls smoothly to the section fragment when you come from another page. (<Link> without scroll props)

It is important to set the following for html in your css.

html {
    --scroll-behavior: smooth !important;
    scroll-behavior: smooth !important;
}

@YoniChechik
Copy link

@timneutkens can you please reopen this issue? a fix is still not available...

@YoniChechik
Copy link

my fix works in prod:

"use client";

import Link from "next/link";
import { usePathname } from "next/navigation";

export default function Heading({ id }: any): JSX.Element {
  const pathname = usePathname();

  return (

      <Link
        href={`${pathname}#${id}`}
        aria-label={anchorTitle}
        title={anchorTitle}
      >
        #
      </Link>
    </As>
  );
}

@clevinson
Copy link

I can confirm the same problems arise for me where locally the anchor links work as expected, but when deployed on netlify, it strips the anchor tag from the URL and reloads hte page:

I am having the same issue. In dev on my local server, scroll to hash-id works. However, once deployed on Netlify it does not work. If I inspect the 'Link' tags ('a' tags) I can click the /#id and it will take me to the correct section of my page.

Also, I cannot seem to get smooth-scrolling to work even on my local server, it just jumps to the /#id. I hope we can find a fix for all this.

It would be great to have this issue re-opened if the issue on netlify is related to this same source problem. Otherwise, I can fine a new issue.

@clevinson
Copy link

clevinson commented Jun 19, 2023

Seems like the issue with netlfiy is that Netlify's latest version of "Next.js Runtime" only supports next v13.4.3 still. I think they've only been fixed as of next v13.4.5.

https://github.com/netlify/next-runtime/releases/tag/next-v1.4.7

Related for tracking: netlify/next-runtime#2089

@timneutkens
Copy link
Member

@YoniChechik if you're talking about <Link href="#somewhere"> that was fixed on May 9th in this PR: #49521, landed in canary on v13.4.5-canary.1 and then on stable in v13.4.5.

@AlmazHecker
Copy link
Contributor

Does it automatically smooth scroll to fragments (hash ids)? Both when coming from another page and for in-page links?

Yes it scrolls smoothly to the section fragment when you come from another page. (<Link> without scroll props)

It is important to set the following for html in your css.

html {
    --scroll-behavior: smooth !important;
    scroll-behavior: smooth !important;
}

u'r a life saver!

@TiagoJeronimo
Copy link

Does it automatically smooth scroll to fragments (hash ids)? Both when coming from another page and for in-page links?

Yes it scrolls smoothly to the section fragment when you come from another page. (<Link> without scroll props)

It is important to set the following for html in your css.

html {
    --scroll-behavior: smooth !important;
    scroll-behavior: smooth !important;
}

This helped! But in my case, I also had to add the prop scroll={false} to the NextLink to make it work:

import NextLink from 'next/link';

<NextLink href={href} scroll={false}>
  {children}
</NextLink>

@vdaguenet
Copy link

@YoniChechik if you're talking about <Link href="#somewhere"> that was fixed on May 9th in this PR: #49521, landed in canary on v13.4.5-canary.1 and then on stable in v13.4.5.

The problem is still happening on v13.4.10.

@timneutkens
Copy link
Member

@vdaguenet please open a new issue and include a reproduction.

@rtrembecky
Copy link

@YoniChechik if you're talking about <Link href="#somewhere"> that was fixed on May 9th in this PR: #49521, landed in canary on v13.4.5-canary.1 and then on stable in v13.4.5.

The problem is still happening on v13.4.10.

I confirm it worked for me on 13.4.6 and it doesn't now when I upgrade to 13.4.10.

@timneutkens
Copy link
Member

#44295 (comment)

@vercel vercel locked and limited conversation to collaborators Jul 24, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Issue was opened via the bug report template. linear: next Confirmed issue that is tracked by the Next.js team.
Projects
None yet
Development

Successfully merging a pull request may close this issue.