Clicking a Link does't scroll to top on the next page #64435
Replies: 67 comments 32 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.
-
This issue is giving me such headaches! I really dont want to update every single page and add some padding/margin ontop. Nextjs plz fix this already! |
Beta Was this translation helpful? Give feedback.
-
Removing |
Beta Was this translation helpful? Give feedback.
-
Hi mates! I haven't tried it, but I guess the problem in this repo would be solved if the fragment was deleted. |
Beta Was this translation helpful? Give feedback.
-
Same problem, when using tabs (nested navigation). Problem exist after place header above tabs. It's not sticky or fixed. After remove header or scroll on previous page more that header height it's works. |
Beta Was this translation helpful? Give feedback.
-
Removing scroll-behavior: smooth!important; from global.css resolved the issue successfully by eliminating the unintended smooth scrolling effect across the entire site. |
Beta Was this translation helpful? Give feedback.
-
I also started having this problem when I was separating my page into a server-rendered From what can I see, the |
Beta Was this translation helpful? Give feedback.
-
Hi everyone— I will be closing this discussion as the explanation for this issue has been added to our documentation → https://nextjs.org/docs/app/api-reference/components/link#scroll (additional comment), a separate issue with sticky headers has been filed, and several solutions have been found (e.g., removing If you find a separate issue that does not follow the current scroll behavior, please feel free to file a new bug report so we can take a look! |
Beta Was this translation helpful? Give feedback.
-
I don't think this is solved tbh. If you close this another one will need to be opened. This isn't stale. |
Beta Was this translation helpful? Give feedback.
-
For me this issue is definitely still not fixed. Other threads which provide a perfect repro link get closed without any further comment, so I won't bother creating a new bug report. If you're curious you can see the behavior even on nextjs.org which was demonstrated perfectly in this thread but for some reason the issue was closed without further explanation. If this is actually expected behavior and the devs don't see any issue then please explain how the behavior on nextjs.org is anything the user would actually expect. To me this seems very odd and is definitely not how I expect the navigation to behave. But it is what it is and we still have to ship awesome stuff, so I had to come up with a fix... What solved the issue in the end for me was adding the following component to the very top of every page. Not the layout but the page itself. export default function ScrollTopFix() {
return (
<div className="text-transparent h-0" aria-hidden="true">
Scrollfix
</div>
);
} The text inside the div is important and it cannot be empty, otherwise it did not work for me. export default function SomePage() {
return (
<>
<ScrollTopFix />
<!-- Page content goes here as usual. -->
</>
);
} Note that I do not have a sticky header like many others say they have. The issue for me boiled down to there not being any content at the top of the page, because most pages had a May this help others who stumble upon this comment... |
Beta Was this translation helpful? Give feedback.
-
I might have a "dirty fix". It's definitely dirty, but I prefer it much more than this useEffect approach. I prefer solutions with html and css over javascript Our situationOur issue comes from the following file structure:
So we have a tall layout file with some kind of profile information and stuff. Then we have different tabs than can be selected and each of them should have a dedicated url for SEO purposes. As a user, when you open up the page first you want to be at the top. When you navigate through the tabs you want to preserve the current scroll position. Btw: This is also why the useEffect approach would break, when we navigate between the tabs... The issueThe issue was same: When I open the page next.js scrolls down to the beginning of the page component and skips all the layout stuff. The reason is rather hidden and you find it in the docs https://nextjs.org/docs/app/building-your-application/routing/linking-and-navigating#scrolling-to-an-id -> "Next.js will scroll to the Page if it is not visible in the viewport upon navigation." The solutionAnd now to the dirty fix: place a hidden div in the page.tsx somewhere top, so it is in the viewport and next does not scroll. In my case route-name/[tab-name]/page.tsx
This now solves the issue that the layout.tsx component will not be overscrolled and the page will be at the top. Now we also change the links from inside the TabsSection in a way that we want to preserve scroll location, because we don't want the user to feel he changes the page. Now they may be an edge case: the user scrolls in a way that the hidden div may be on the top outside of the viewport, in this case it would scroll up when we select another tab. We can fix this by disabling the scroll for the tab links:
Let me know if it works for you. Good luck with this! 🍀🍀🍀 |
Beta Was this translation helpful? Give feedback.
-
I have tried many / all of the suggestions posted here, and I am still experiencing this issue the majority of the time during forward navigations. Having a setting in the component to scroll to top would be great and avoid the need for additional code to resolve (I really don't want to use a useEffect) In my opinion, implementing black box logic inside the
Another interesting point on this topic - I have yet to see this "scroll bug" occur a single time in Firefox, yet in Chrome it occurs the majority of the time when clicking on a while on a page that is already scrolled. Firefox behaves the way I would expect, the forward navigations take me to the top of the page. |
Beta Was this translation helpful? Give feedback.
-
the only answer to this is to opt-out from using Link to just use edit: i am really sorry if this might be out of context from Link component usage, but this is my only answer outside of making a client component with useEffect that scroll to 0 window viewport, because this useEffect solution is bad idea since refreshing the page will also scroll it to 0 window viewport. i also try with other solutions such as making every components wrapper with fragment or add a empty div at the top page or layout, but again, not really my likings on using dirty-hack in case this could be fixed in the future. |
Beta Was this translation helpful? Give feedback.
-
This handles the scroll to the top issue while preserving the scroll position on navigation change with the browser's back and forward buttons. I had to add 'use client';
import {useEffect, useRef} from 'react';
import {usePathname} from 'next/navigation';
export default function Navbar() {
const pathname = usePathname();
const ref = useRef<HTMLElement>(null);
const isStatePopped = useRef(false);
// Handling the scroll position to ensure clicking on the links
// scrolls the page to the top with the sticky positioned navbar.
useEffect(() => {
const onPopState = () => (isStatePopped.current = true);
window.addEventListener('popstate', onPopState);
return () => window.removeEventListener('popstate', onPopState);
}, []);
useEffect(() => {
if (!isStatePopped.current) {
// navigation occurred without pressing
// the browser's back or forward buttons
ref.current!.scrollIntoView();
} else {
isStatePopped.current = false;
}
}, [pathname]);
return (
<nav
ref={ref}
style={{
position: 'sticky',
height: '5rem',
scrollMarginTop: '5rem'
}}></nav>
);
} |
Beta Was this translation helpful? Give feedback.
-
It's 2025, fix the bug. We don't want PPR we want proper scroll on navigation. Normal web stuff. It's easily reproducible, and it is extremely sporadic. Using a Loading.tsx causes issues that don't occur if that doesn't exist. Nested layouts cause issues. There are so many issues. Just provide a scrollToTop in next.config.json and save us the trouble. Please? What's it gonna cost |
Beta Was this translation helpful? Give feedback.
-
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