For any one using Next 13 (App dir) and above and want to be able to scroll to specific section on the current page or navigate to another page and automaticaly scroll to a specific section and like me has tried everything here and didn't work then checkout this answer.
Firstly Create a Function that Accept a Id String and Scroll the element Into View.
// Handles scrolling of Element to view
export function scrollElementToView(scrollToId: string) {
const element = document.querySelector(`#${scrollToId}`) as HTMLElement;
const elRect = element.getBoundingClientRect();
const scrollDistance = elRect.top + window.scrollY;
// Incase you want to offset the scroll To view Position.
const offset = Number(element.getAttribute('data-scroll-to-view-offset')) || 0;
window.scrollTo({
top: scrollDistance + offset,
behavior: 'smooth'
})
}
Then create a file name ScrollToLinkGlobalComponent and add
'use client';
import React, { useEffect } from 'react';
import { useSearchParams } from 'next/navigation';
import { scrollElementToView } from //whereEver you keep the file
function ScrollToLinkGlobalComponent() {
const searchParams = useSearchParams();
useEffect(() => {
// get element Id from searchParams
const scrollToId = searchParams.get("scrollToId");
if (!scrollToId) return; // return if there is none
scrollElementToView(scrollToId);
}, [searchParams])
return null
}
export default ScrollToLinkGlobalComponent
Now Add ScrollToLinkGlobalComponent to your RootLayout. Its should look something like this
*** Imports ***
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en" className={nunitoSansFont.className}>
<body>
{children}
<ScrollToLinkGlobalComponent />
</body>
</html>
)
}
Then Create a ScrollToLink Component that extends the default Next Link Component.
'use client';
import Link from 'next/link';
import React, { ComponentPropsWithRef } from 'react';
import { useSearchParams } from 'next/navigation';
import { scrollElementToView } from //whereEver you keep the file
interface PropTypes extends ComponentPropsWithRef<typeof Link> {
scrollToId: string
}
function ScrollToLink({ children, scrollToId, href, ...props }: PropTypes) {
const searchParams = useSearchParams();
const persistScrollFeature = () => {
const urlScrollToId = searchParams.get("scrollToId");
if (!urlScrollToId || scrollToId !== urlScrollToId) return; //let the Global Component Handle it
scrollElementToView(urlScrollToId);
}
return (
<Link {...props}
onClick={persistScrollFeature}
href={`${href}?scrollToId=${scrollToId}`}
scroll={false} //very important, its disable nextJs scroll To top on navigation feature
>
{children}
</Link>
)
}
export default ScrollToLink
Now in any of your Component you can import and use ScrollToLink like this.
<ScrollToLink href="/" scrollToId='hero-section'>Hero Section</ScrollToLink>
<ScrollToLink href="/" scrollToId='Testimony-section'>Hero Section</ScrollToLink>
<div id='hero-section'>I am Hero Section</div>
<div id='Testimony-section'>I am Hero Section</div>
//You can offset the scrollToView position like this
<div id='Testimony-section' data-scroll-to-view-offset='-200'>I am Hero Section</div>