Skip to main content

useScrollRestoration

A custom hook that saves and restores the previous scroll position of the browser or a specific element.

By default, it manages the window scroll, and you can use ref to manage a specific scrollable area.


Codeโ€‹

๐Ÿ”— View source code


Interfaceโ€‹

typescript
interface UseScrollRestorationOptions {
id?: string;
enabled?: boolean;
behavior?: ScrollOptions['behavior'];
retry?: number;
}

function useScrollRestoration<T extends HTMLElement>({
id,
enabled,
behavior,
}?: UseScrollRestorationOptions): {
ref: React.RefObject<T | null>;
};

Optionsโ€‹

NameTypeDefaultDescription
idstring'window' or 'element'Scroll restoration identifier (required when using multiple instances)
enabledbooleantrueWhether scroll restoration is enabled
behaviorScrollOptions['behavior']'instant'Scroll restoration behavior ('auto', 'instant', 'smooth')
retrynumber5Number of scroll restoration retries (with exponential backoff)

Remarksโ€‹

Caution
  • When content is loaded asynchronously or due to image loading, the page height may be smaller than the saved scroll position.
    • In such cases, it automatically retries using exponential backoff, increasing the retry interval each time.
    • Retry intervals: 100ms โ†’ 200ms โ†’ 400ms โ†’ 800ms โ†’ 1600ms
    • The default maximum number of retries is 5, which can be adjusted with the retry option.
  • When using the hook multiple times within a single component, assign explicit id options to distinguish each instance.
    • If no id is specified, the default is 'element' when a ref is present, or 'window' otherwise.
    • Without an id, duplicate keys may cause scroll restoration to malfunction โ€” use with caution.
  • Scroll position is saved on refresh, page navigation (back/forward), and hook unmount.
    • Therefore, if this hook is called in a component that remains mounted without unmounting (e.g., a Layout component), scroll restoration may not work correctly.
  • If the URL has a hash fragment (#section), scroll restoration is skipped.
    • This is to respect the user's clear intent for hash scrolling and to avoid conflicts with standard browser behavior.

When using React Router
  • React Router provides its own location.key.
  • useScrollRestoration automatically detects and uses it.
  • Per-page scroll positions are accurately restored without any additional configuration.

Usageโ€‹

Window Scrollโ€‹

typescript
import { useScrollRestoration } from '@modern-kit/react';

const Page = () => {
useScrollRestoration();

return (
<div>
{/* Long content... */}
</div>
);
};

Specific Elementโ€‹

typescript
import { useScrollRestoration } from '@modern-kit/react';

const ScrollBox = () => {
const { ref } = useScrollRestoration<HTMLDivElement>();

return (
<div
ref={ref}
style={{ height: '500px', overflowY: 'auto' }}
>
{/* Content with internal scrolling */}
</div>
);
};

Multiple Instancesโ€‹

When managing multiple scroll areas in a single component, assign a unique id to each instance.

typescript
import { useScrollRestoration } from '@modern-kit/react';

const MultiScrollPage = () => {
useScrollRestoration(); // window scroll (default id: 'window')
const { ref: sidebarRef } = useScrollRestoration<HTMLDivElement>({ id: 'sidebar' });
const { ref: contentRef } = useScrollRestoration<HTMLDivElement>({ id: 'content' });

return (
<div>
<aside ref={sidebarRef} style={{ height: '100vh', overflowY: 'auto' }}>
{/* Sidebar content */}
</aside>
<main ref={contentRef} style={{ height: '100vh', overflowY: 'auto' }}>
{/* Main content */}
</main>
</div>
);
};

Example (Window Scroll)โ€‹

1. Scroll the page about halfway down.

2. Click the browser's 'Back' button.

3. Check that the scroll position is preserved.


Example1 (Scroll Restoration for a Specific Element)โ€‹

A basic example for managing the scroll position of a specific area (e.g., a scrollable container).

Use ref to specify the element whose scroll position should be restored.

๐Ÿงช How to test

  1. Scroll the scroll box below about halfway (e.g., to around "Item 10")
  2. Click the browser's 'Back' button
  3. When you return, the scroll position is exactly restored! โœจ
๐Ÿ“Œ Item 1
๐Ÿ“Œ Item 2
๐Ÿ“Œ Item 3
๐Ÿ“Œ Item 4
๐Ÿ“Œ Item 5
๐Ÿ“Œ Item 6
๐Ÿ“Œ Item 7
๐Ÿ“Œ Item 8
๐Ÿ“Œ Item 9
๐Ÿ“Œ Item 10
๐Ÿ“Œ Item 11
๐Ÿ“Œ Item 12
๐Ÿ“Œ Item 13
๐Ÿ“Œ Item 14
๐Ÿ“Œ Item 15
๐Ÿ“Œ Item 16
๐Ÿ“Œ Item 17
๐Ÿ“Œ Item 18
๐Ÿ“Œ Item 19
๐Ÿ“Œ Item 20
๐Ÿ’ก Key point: The scroll position of a specific element is tracked via ref. The behavior: 'smooth' option applies a smooth restoration animation.

Example2 (Scroll Restoration After Async Data Loading)โ€‹

In real applications, data is often loaded asynchronously.

This example demonstrates that the scroll position is accurately restored even during data loading.

Internally, a retry mechanism (up to 5 times, with exponential backoff) waits until the content is fully loaded.

๐Ÿงช How to test

  1. Wait for the data to load (1 second)
  2. Scroll the scroll box about halfway
  3. Click the browser's 'Back' button
  4. When you return:
    • "Loading..." is shown โ†’ data finishes loading
    • The scroll position is automatically restored to the previous position! ๐ŸŽฏ
โณ Loading...
๐Ÿ’ก Key point: If the content height is insufficient, it automatically retries.
  • Retry intervals: 100ms โ†’ 200ms โ†’ 400ms โ†’ 800ms โ†’ 1600ms
  • Max retries: 5 (default, configurable via the retry option)

Example3 (Multiple Instances)โ€‹

When multiple scroll areas need to be managed independently on a single page, assign a unique id to each instance.

For example, when a sidebar and a main content area each have independent scrolling:

๐Ÿงช How to test

  1. Scroll the blue area (main content) about halfway.
  2. Scroll the green area (sidebar) about halfway.
  3. Click the browser's 'Back' button.
  4. When you return, both areas have their scroll positions accurately restored! โœจ
๐Ÿ“„ Main Content (id: "main-content")
Content Item 1
Content Item 2
Content Item 3
Content Item 4
Content Item 5
Content Item 6
Content Item 7
Content Item 8
Content Item 9
Content Item 10
Content Item 11
Content Item 12
Content Item 13
Content Item 14
Content Item 15
Content Item 16
Content Item 17
Content Item 18
Content Item 19
Content Item 20
๐Ÿ“‘ Sidebar Menu (id: "sidebar-menu")
Menu Item 1
Menu Item 2
Menu Item 3
Menu Item 4
Menu Item 5
Menu Item 6
Menu Item 7
Menu Item 8
Menu Item 9
Menu Item 10
Menu Item 11
Menu Item 12
Menu Item 13
Menu Item 14
Menu Item 15
Menu Item 16
Menu Item 17
Menu Item 18
Menu Item 19
Menu Item 20
๐Ÿ’ก Key point: Each area's scroll position is saved and restored independently. This is because the id option distinguishes each instance.