Stopping the Jump: Managing Scroll Position with Inertia.js visit()
Learn how to use the preserveScroll option in Inertia.js to prevent the browser from jumping to the top of the page during data updates and partial reloads.
23 Apr 2026, 04:33 UTC

The "Jump" Problem in Single Page Apps
You have a long page—perhaps a data table or a feed—and the user clicks a button to update a record or filter a list. The request completes, the component updates, and suddenly the browser snaps back to the top of the page. The user has lost their place, and they now have to scroll all the way back down to see if their action actually worked.
In a traditional multi-page application, a full reload always resets the scroll. In an Inertia.js application, you have more control. The goal is to perform a navigation or a data refresh without disrupting the user's vertical position on the screen.
How preserveScroll Works
Inertia.js provides a preserveScroll option within its visit method (and the Link component). When set to true, Inertia captures the current window scroll offset before initiating the request. Once the server responds and the new component is rendered, Inertia restores that exact offset.
This is particularly useful for partial reloads. A partial reload updates only specific pieces of data (props) without re-rendering the entire page layout, but by default, Inertia may still treat the navigation as a "new page" and reset the scroll. Enabling preserveScroll ensures the transition is invisible to the user's viewport.
Practical Implementation
Depending on whether you are using a declarative link or a programmatic call, the implementation differs slightly. Below is a comparison of how to handle a "Like" button or a "Filter" update where you want the user to stay exactly where they are.
Programmatic Visit
Use the router.visit method when the navigation is triggered by a function (e.g., inside a handleSubmit or a custom event listener). Run this in your frontend component (Vue, React, or Svelte):
import { router } from '@inertiajs/vue3'
const updateStatus = () => {
router.visit('/settings/update', {
method: 'patch',
data: { status: 'active' },
preserveScroll: true, // Prevents the page from jumping to top
preserveState: true, // Keeps current component state (like input values)
})
}
Declarative Link
If you are using the Link component, pass the prop directly to the element:
<Link
href="/refresh-data"
method="get"
preserve-scroll
>
Refresh List
</Link>
Comparison: Default vs. Preserved Scroll
| Behavior | Default (false) | preserveScroll: true |
|---|---|---|
| Window Position | Resets to (0, 0) | Maintains current Y-offset |
| User Experience | Disorienting on long pages | Seamless, feels like an API call |
| Best Use Case | Navigating to a new page | Filtering, liking, or updating a row |
Critical Limitations
It is important to understand that preserveScroll only manages the global window scroll. If your application uses a nested scrollable area (for example, a div with overflow-y: auto), Inertia cannot track or restore the scroll position of that specific element. You would need to manually store that element's scrollTop value in a state manager or a ref and re-apply it after the Inertia visit completes.
Additionally, if you are using Server-Side Rendering (SSR), the initial page load is handled by the server. The scroll restoration only kicks in during client-side navigations after the app has hydrated.
Verification Steps
To verify this is working as intended:
- Open your browser's Developer Tools and go to the Network tab.
- Scroll halfway down a long page.
- Trigger the visit with
preserveScroll: true. - Confirm the request completes (look for the
X-Inertiaheaders) and observe that the viewport does not move. - Repeat the process with
preserveScroll: falseto confirm the page snaps to the top.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.