FG
๐ŸŒ Web & Full-StackVercel

[NEXT-1147] Scroll position is reset when search params are updated

Freshabout 19 hours ago
Mar 14, 20260 views
Confidence Score95%
95%

Problem

Verify canary release - [X] I verified that the issue exists in the latest Next.js canary release Provide environment information [code block] Link to the code that reproduces this issue https://codesandbox.io/p/sandbox/modest-gould-h4nlvd?file=%2Fapp%2Fpage.tsx&selection=%5B%7B%22endColumn%22%3A1%2C%22endLineNumber%22%3A7%2C%22startColumn%22%3A1%2C%22startLineNumber%22%3A7%7D%5D To Reproduce Click the counter to update client state. Scroll a little. Click the second button to update search params. The client state is preserved, but the scroll position is lost. Describe the Bug Context In Next <= 13.2.4, updating search params was working as intended : client state was kept, and scroll position was kept too. In Next 13.2.5, a regression made the client components unmount and remount when search params were updated. @feedthejim fixed that unmounting in https://github.com/vercel/next.js/pull/49047 (it's testable on `13.3.5-canary.2`), but there is still an issue now: scroll position is lost on search params updates. Problem: persisting state in search params is very important with the App router - that's a clean way for client components to request updated data from the RSC. Reproduction Codesandbox Here's a codesandbox reproducing the bug: https://codesandbox.io/p/sandbox/modest-gould-h4nlvd?file=%2Fapp%2Fpage.tsx&selection=%5B%7B%22endColumn%22%3A1%2C%22endLineNumber%22%3A7%2C%22startColumn%22%3A1%2C%22startLineNumber%22%3A7%7D%5D In video https://user-

Unverified for your environment

Select your OS to check compatibility.

1 Fix

Canonical Fix
Unverified Fix
New Fix โ€“ Awaiting Verification

Persist Scroll Position on Search Params Update in Next.js

Medium Risk

In Next.js versions 13.2.5 and later, updating search parameters causes a remount of client components, which results in the loss of scroll position. This behavior is due to the way the App Router handles state and component lifecycle during URL updates.

Awaiting Verification

Be the first to verify this fix

  1. 1

    Store Scroll Position

    Before updating the search parameters, capture the current scroll position and store it in a state variable. This allows you to restore the scroll position after the component remounts.

    typescript
    const [scrollPos, setScrollPos] = useState(0);
    
    const handleUpdateSearchParams = () => {
      setScrollPos(window.scrollY);
      // Update search params logic here
    };
  2. 2

    Restore Scroll Position

    After the component has remounted, use the stored scroll position to scroll back to the previous position. This can be done using the useEffect hook to trigger the scroll restoration after the component updates.

    typescript
    useEffect(() => {
      window.scrollTo(0, scrollPos);
    }, [scrollPos]);
  3. 3

    Debounce Scroll Position Capture

    To improve performance and avoid excessive state updates, debounce the scroll position capture. This can be achieved using a simple debounce function that limits how often the scroll position is recorded.

    typescript
    const debounce = (func, delay) => {
      let timeout;
      return (...args) => {
        clearTimeout(timeout);
        timeout = setTimeout(() => func.apply(this, args), delay);
      };
    };
    
    const handleScroll = debounce(() => setScrollPos(window.scrollY), 100);
    
    useEffect(() => {
      window.addEventListener('scroll', handleScroll);
      return () => window.removeEventListener('scroll', handleScroll);
    }, []);
  4. 4

    Test Scroll Persistence

    After implementing the above changes, test the application by updating the search parameters and verifying that the scroll position is maintained. Ensure that both the client state and scroll position persist across updates.

    none
    // No specific code needed, just manual testing required.

Validation

Confirm that after updating the search parameters, the scroll position remains consistent and does not reset. Perform multiple updates to ensure the behavior is stable across different interactions.

Sign in to verify this fix

Environment

Submitted by

AC

Alex Chen

2450 rep

Tags

nextjsreactssrbuglinear:-next