next-scroll-restorer handles scroll restoration for Next.js apps built with app directory. Fixed bugs that Next.js team ignored.
Important! This component works only for application written with Next.js 'app' directory.
npm install next-scroll-restorer
for NPMpnpm add next-scroll-restorer
for pnpmyarn add next-scroll-restorer
for Yarn
- 100% of codebase written in Typescript
- Can be used at any nesting
layout.tsx
file. Root layout isn't required. - Fixed bug where built-in Next.js scroll restoration is not immediate
- Fixed annoying bug where scroll position forgotten by Next.js built-in scroll restoration.
- Extensive testing in different browsers with Playwright testing library.
Before you start, keep in mind following rules.
- Keep disabled native
scrollRestoration
option in Next.js config to avoid conflicts. - Skip this rule for Next.js 14.1.0 and higher. In case your Next.js version is less than 14.1.0 then you should enable
windowHistorySupport
in your Next.js config underexpermimental
property. Since Next.js 14.1.0 browser history support is enabled by default.
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental:{
//Only For Next.js versions prior to 14.1.0 because it is enabled by default since version 14.1.0
windowHistorySupport:true
},
}
module.exports = nextConfig
Create component named ClientSideScrollRestorer
in your src
directory with useScrollRestorer
hook and "use client"
directive to prevent server errors.
src/ClientSideScrollRestorer.tsx
"use client"
import {useScrollRestorer} from 'next-scroll-restorer';
const ClientSideScrollRestorer = () => {
useScrollRestorer()
return <></>
}
export default ClientSideScrollRestorer
Import component created in a previous step to your root layout file (layout.tsx).
Wrap it wih React <Suspense/>
to avoid possible client-side deopting for entire page.
app/layout.tsx
import ClientSideScrollRestorer from '../src/ClientSideScrollRestorer'
import {ReactNode, Suspense} from "react";
type Props = {
children: ReactNode
}
const RootLayout = ({children}) => {
return (
<html lang="uk">
<body>{children}</body>
<Suspense>
<ClientSideScrollRestorer/>
</Suspense>
</html>
)
}
export default RootLayout
It can be any nesting layout shared by group of routes in case you do not want to enable scroll restoration for the whole application.