Clicking a Link does't scroll to top on the next page #64435
Replies: 86 comments 42 replies
-
Duplicate of #42492 |
Beta Was this translation helpful? Give feedback.
-
Not a duplicate. Still not fixed. |
Beta Was this translation helpful? Give feedback.
-
I'm having the same issue. I really don't want to turn Layout into a client component. |
Beta Was this translation helpful? Give feedback.
-
Ok, I just did a |
Beta Was this translation helpful? Give feedback.
-
I'm having the same issue, are you saying that |
Beta Was this translation helpful? Give feedback.
-
I face the same issue and made a demo here: https://demolink.vercel.app/ Repo: https://github.com/h2toan/next-link-does-not-scroll-to-top Step to investigate: Try to scroll down to the bottom, then click in the Link at green box. It only navigates to the gray box, not the top of page (the red and orange box) My theory is that in /app router, it only scrolls to the top component of the current layout (the top of {children} in layout.js), and if there are some components above {children}, they might not be displayed on the screen (if the total height is greater the height of screen, like 100vh?) |
Beta Was this translation helpful? Give feedback.
-
I encountered this issue but I figured out the cause is I use a custom scroll container instead of window or document. |
Beta Was this translation helpful? Give feedback.
-
The issue happens when you have a sticky header. here's a fix: 'use client'
export default function Scroll() {
// when clicking a link, user will not scroll to the top of the page if the header is sticky.
// their current scroll position will persist to the next page.
// this useEffect is a workaround to 'fix' that behavior.
const pathname = usePathname();
useEffect(() => {
window.scroll(0, 0);
}, [pathname]);
return <></>;
} Put the component in your export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<Scroll />
<body>{children}</body>
</html>
);
} problem solved until they fix it. |
Beta Was this translation helpful? Give feedback.
-
Still not fixed in the current version 13.5.6. |
Beta Was this translation helpful? Give feedback.
-
Still not fixed for me either. Having to use a |
Beta Was this translation helpful? Give feedback.
-
Next 14.0.3 still not fixed |
Beta Was this translation helpful? Give feedback.
-
For what it's worth, scrolling works well on the latest versions of Safari and Firefox for iOS (-- and has always worked well on these apps, at least with my project), but is random on Chromium (Chrome version 119.0.6045.169, Edge version 119.0.2151.105) for iOS. Impossible to narrow down the root cause as it seems to be completely random. Furthermore, I have never had any issues with Chromium for MacOs. |
Beta Was this translation helpful? Give feedback.
-
The issue seems to be related to the state of iOS Safari viewport. Since there is two states that the viewport can be at: small and large - the issue only appears when clicking Link at large viewport state. If I scroll down the page and then slightly up to get my viewport back to small state - and click the Link - the next page opens up at the top. It is visible at the Timelines - on the initial page open - the browser is at small state, and then changes to large state, I am assuming, to get back to the state it had been before the click. Using normal anchor tag, instead of Link router, works for me, since browser just refreshes the page. iOS_issue.MP4 |
Beta Was this translation helpful? Give feedback.
-
bump - facing same issue with [email protected], using a global style for the body "min-h-screen" and when the viewport shrinks in height and is not 100vh anymore the issue happens. For example I have a page with return <></> until the content is loaded, had to return div with h-screen in loading state as well to "fix" the issue |
Beta Was this translation helpful? Give feedback.
-
I just ran into the same issue. I had some "modal" components placed further down in the DOM than my main content, and it looks like Safari was jumping down to them to focus them instead of staying at the top of the page. Placing them higher in the DOM fixed the issue, so my suggestion is to also check out what elements you're rendering below the rest of your content. |
Beta Was this translation helpful? Give feedback.
-
I have the same exact problem, the workaround as mentioned above is to use a hidden client component with a useEffect and scrollTo but I'm tired of fixing Nextjs's unwanted bugs in this fashion. |
Beta Was this translation helpful? Give feedback.
-
Zzzzzzzz |
Beta Was this translation helpful? Give feedback.
-
How am I supposed to maintain confidence in Vercel hosting if you guys won’t even talk about fixing something as basic as this |
Beta Was this translation helpful? Give feedback.
-
So I tried this workaround:
It works great for scrolling, but after adding this, I started getting the "Rendered more hooks than during the previous render" error, but only on my not-found.tsx page. The issue occurs only on Safari and on mobile devices in any browser, but it's intermittent — sometimes the page loads fine, and other times it throws the error. Does anyone have an idea why this might be happening or how to fix it? |
Beta Was this translation helpful? Give feedback.
-
Why are you hosting a hackathon instead of fixing this? @leerob |
Beta Was this translation helpful? Give feedback.
-
I narrowed this down to a known rendering/ A simple workaround is to comment out the // Verify if the element is a HTMLElement and if we want to consider it for scroll behavior.
// If the element is skipped, try to select the next sibling and try again.
while (!(domNode instanceof HTMLElement) || shouldSkipElement(domNode)) {
- if (process.env.NODE_ENV !== 'production') {
+ // if (process.env.NODE_ENV !== 'production') {
if (domNode.parentElement?.localName === 'head') {
// TODO: We enter this state when metadata was rendered as part of the page or via Next.js.
// This is always a bug in Next.js and caused by React hoisting metadata.
// We need to replace `findDOMNode` in favor of Fragment Refs (when available) so that we can skip over metadata.
+ domNode = domNode.parentElement
}
- }
+ // }
// No siblings found that match the criteria are found, so handle scroll higher up in the tree instead.
if (domNode.nextElementSibling === null) { You can apply this patch locally using |
Beta Was this translation helpful? Give feedback.
-
It might be worth mentioning - my issue was related to either Framer Motion, or a Carousel component. Regularly, the Link has expected behaviour, however, putting the Link inside a carousel would give the unintended scroll behaviour. A regular anchor tag worked. What I believe to be the issue was something that the Carousel had, which made the intended scroll behaviour of Link affect the Link's parent, rather than the whole page. While not ideal, I just used router.push(link) on that button. I am sure there could be a less band-aid-y solution, but I suspect checking for parent scroll behaviour or overflow or something along those lines might help. |
Beta Was this translation helpful? Give feedback.
-
FWIW I was experiencing the same issue as #64435 (comment) on Chrome on iOS only when clicking a link from the bottom of the page, which led me to this discussion. It was dependent on whether Chrome's browser UI chrome was either collapsed or expanded, but clicking a link at the bottom of the page would either be at the top or ~100px from the top. It turned out the cause was the I'd hazard a guess that there's a moment in between navigation when the DOM is replaced on the page where the height changes, and the browser's scroll position ends up as the difference between 100vh and the visible viewport upon navigation (100svh) when setting a min-height of 100vh. Putting this out there for anyone experiencing this issue specifically, as it seems like there are a few different loosely related issues in this discussion. |
Beta Was this translation helpful? Give feedback.
-
15.3.5 bug still present. Is there a ticket for this? |
Beta Was this translation helpful? Give feedback.
-
Came across this bug when using the below component in a
The bug is gone if I don't dynamically generate multiple items, so I guess the problem is with the app not knowing in advance the array length or something? Fixed by dropping |
Beta Was this translation helpful? Give feedback.
-
I end up using <button
key={key}
onClick={() => {
window.location.assign(`/services/${service.slug}`)
}}
>
///...
</button> |
Beta Was this translation helpful? Give feedback.
-
If you manage to build a glowing hammer (keeping scroll position on navigating back), but the hammer can’t actually drive a nail into the wall (doing normal So for now, I have to fall back to simple |
Beta Was this translation helpful? Give feedback.
-
This is still present in |
Beta Was this translation helpful? Give feedback.
-
I was experiencing this same issue verbatim. I discovered it was the styling in my loading.js template. the height of the loading template appears to be added to the overall height of the page or it is altering the scroll top position. I noticed in the code that @IonelLupu posted about this issue there was not a loading template so I added one with no height and reloaded and the issue was resolved. so maybe this has something to do with the default nextjs loading state. added loading template: https://stackblitz.com/edit/vercel-next-js-m9zvip3d?file=package.json |
Beta Was this translation helpful? Give feedback.
-
❌ This is still present in 15.5.4 |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Verify canary release
Provide environment information
Which area(s) of Next.js are affected? (leave empty if unsure)
App directory (appDir: true), Routing (next/router, next/navigation, next/link)
Link to the code that reproduces this issue
https://stackblitz.com/edit/vercel-next-js-x6badq?file=package.json
To Reproduce
2nd related issue
Now, remove the font instance (
const inter = Inter({ subsets: ['latin'] });
) from the second page. Test the app again and notice the scroll bar being at the top but still has a small scroll. It's nottop: 0
. (Reproduction link)Temporary workaround
In order to fix this issue, for now, I added this in my main layout.tsx file. I had to convert it to a client component:
Describe the Bug
Clicking on a link that redirects to a page makes the scroll bar be scrolled instead of having it at the top of the page. Requirements of the bug are described above
Expected Behavior
Expecting the scroll bar to be at the top without removing the font instance from the 2nd page.
To note that, on my real app, I don't have this font instance. Si the bug is probably introduced by some other package.
Which browser are you using? (if relevant)
Chrome 109
How are you deploying your application? (if relevant)
next start
Beta Was this translation helpful? Give feedback.
All reactions