One automatically handles scroll restoration to provide a native browser-like experience. When you navigate to a new page, scroll resets to the top. When you press back/forward, your previous scroll position is restored.
The ScrollBehavior component lets you customize this behavior for special cases.
Out of the box, One handles:
#element-id when present in the URLYou don’t need to add ScrollBehavior unless you want to change this behavior.
Add ScrollBehavior once in your root layout:
app/_layout.tsx
import { ScrollBehavior, Slot } from 'one'
export default function Layout() {
return (
<>
<ScrollBehavior />
<Slot />
</>
)
}
| Prop | Type | Default | Description |
|---|---|---|---|
disable | boolean | 'restore' | false | Disable scroll behavior entirely or just restoration |
// Disable all scroll behavior
<ScrollBehavior disable />
// Disable only scroll restoration on back/forward
// (still scrolls to top on new navigation)
<ScrollBehavior disable="restore" />
For complex layouts like tabs or dashboards, you may want child routes to share a scroll position. Use the useScrollGroup hook to create a scroll group:
app/dashboard/_layout.tsx
import { Slot, useScrollGroup } from 'one'
export default function DashboardLayout() {
// All routes under /dashboard will share scroll position
useScrollGroup()
return (
<div>
<Sidebar />
<main>
<Slot />
</main>
</div>
)
}
Now when navigating between /dashboard/overview and /dashboard/settings, the scroll position is preserved instead of resetting to the top.
You can specify a custom group path:
import { useScrollGroup } from 'one'
function MyLayout() {
// Create a scroll group for a specific path
useScrollGroup('/products')
return <Slot />
}
Disable scroll behavior for individual navigation actions using the scroll prop on Link:
import { Link } from 'one'
// This navigation won't affect scroll position
<Link href="/next-page" scroll={false}>
Stay in place
</Link>
Or with useRouter:
import { useRouter } from 'one'
function MyComponent() {
const router = useRouter()
const handleClick = () => {
router.push('/next-page', { scroll: false })
}
return <button onClick={handleClick}>Navigate without scrolling</button>
}
#section), scrolls to that elementScroll positions are stored per-pathname in sessionStorage, so they persist across page refreshes within a session but not across browser sessions.
| Platform | Support |
|---|---|
| Web | Full support |
| React Native | Not applicable (no window scroll) |
ScrollBehavior is a web-only component. On native platforms, scroll restoration is handled by the native scroll views themselves.
Preserve scroll position when users navigate away and back:
app/feed/_layout.tsx
import { Slot, useScrollGroup } from 'one'
export default function FeedLayout() {
// Preserve scroll in the feed when viewing details
useScrollGroup()
return <Slot />
}
Keep each tab’s scroll position independent:
app/(tabs)/_layout.tsx
import { Tabs, useScrollGroup } from 'one'
export default function TabLayout() {
useScrollGroup()
return (
<Tabs>
<Tabs.Screen name="home" />
<Tabs.Screen name="search" />
<Tabs.Screen name="profile" />
</Tabs>
)
}
Don’t scroll when opening modals:
<Link href="/modal" scroll={false}>
Open Modal
</Link>
Edit this page on GitHub.