withInMemoryScrolling in Angular: Modern Scroll Restoration and Anchor Scrolling Explained
Source: Dev.to
Introduction
Single‑page applications (SPAs) fundamentally changed web navigation by eliminating full page reloads. However, this architectural shift introduced a UX regression: browsers lost their native ability to restore scroll positions when users navigate backward or forward through history.
In traditional multi‑page applications, the browser automatically remembers where you scrolled on a page and returns you to that exact position when you hit the back button. SPAs broke this behavior.
Angular’s withInMemoryScrolling router feature addresses this gap by giving the router control over scroll behavior during navigation. It provides declarative configuration for two critical scroll‑related features:
- Scroll position restoration across route changes.
- Fragment‑based anchor scrolling.
Why you should care
If your app frequently navigates between routes and users expect intuitive scroll behavior—think content‑heavy sites, e‑commerce list‑detail pages, dashboards with tabbed interfaces, or any application where preserving navigation context improves usability—this feature is essential.
What Is withInMemoryScrolling?
withInMemoryScrolling is a router‑feature function introduced in Angular’s standalone‑API era. It enables the router to manage scroll behavior during navigation events. The “in‑memory” designation refers to how Angular stores scroll positions: it maintains a map of scroll positions in memory, keyed by navigation state, rather than relying on browser‑native mechanisms.
How it works
- Navigation start – the router captures the current scroll position.
- Navigation end – based on the configuration, the router decides to:
- restore a previous position,
- scroll to the top,
- scroll to an anchor (fragment), or
- leave the scroll unchanged.
The decision depends on options such as scrollPositionRestoration and anchorScrolling, as well as the navigation context (forward navigation, back navigation, route reload, etc.).
Browser default vs. Angular router‑controlled scrolling
| Aspect | Browser default | Angular router‑controlled |
|---|---|---|
| Scope | Document‑level, may be unreliable when the DOM is replaced. | Operates within the router lifecycle, aware of lazy loading and route transitions. |
| Determinism | Can be inconsistent in SPAs. | Provides deterministic behavior across all navigation scenarios. |
| Customization | Limited. | Fully configurable via withInMemoryScrolling. |
Setting It Up (Standalone API)
Basic configuration
import { ApplicationConfig } from '@angular/core';
import { provideRouter, withInMemoryScrolling } from '@angular/router';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(
routes,
withInMemoryScrolling({
scrollPositionRestoration: 'enabled', // or 'disabled' | 'top'
anchorScrolling: 'enabled' // or 'disabled'
})
)
]
};
Standalone bootstrap approach
import { bootstrapApplication } from '@angular/platform-browser';
import { provideRouter, withInMemoryScrolling } from '@angular/router';
import { AppComponent } from './app/app.component';
import { routes } from './app/app.routes';
bootstrapApplication(AppComponent, {
providers: [
provideRouter(
routes,
withInMemoryScrolling({
scrollPositionRestoration: 'enabled',
anchorScrolling: 'enabled'
})
)
]
});
Configuration Options
| Option | Type | Description |
|---|---|---|
scrollPositionRestoration | 'disabled' | 'enabled' | 'top' | Controls how scroll positions are managed during navigation. |
anchorScrolling | 'enabled' | 'disabled' | Enables/disables automatic scrolling to fragment identifiers (#anchor). |
scrollPositionRestoration values
| Value | Behaviour | Typical Use Cases | UX Implications |
|---|---|---|---|
disabled | No scroll management; the page stays where it was during navigation. | * Custom scroll logic * Infinite‑scroll components that manage their own position * Single‑route apps where restoration isn’t needed * Performance‑critical apps minimizing router overhead | Users navigating back will see the top of the previous page, which can feel disorienting in list‑detail patterns. |
enabled | Restores the previous scroll position on back/forward navigation; scrolls to top on new forward navigations. | * E‑commerce product listings * CMS article pages * Any list‑to‑detail navigation pattern | Provides an intuitive experience that matches traditional websites—users return to where they left off. |
top | Always scrolls to the top of the page on every navigation, regardless of direction. | * Landing‑page flows where each view should start at the top * Wizard‑style step‑by‑step interfaces | Guarantees a fresh start on each route change, but loses context when navigating back. |
Example: Disable scroll restoration
withInMemoryScrolling({
scrollPositionRestoration: 'disabled'
});
Example: Enable scroll restoration
withInMemoryScrolling({
scrollPositionRestoration: 'enabled'
});
Anchor Scrolling
When anchorScrolling is set to 'enabled', the router automatically scrolls to the element whose id matches the URL fragment (e.g., #section-2). This works together with scrollPositionRestoration—if you navigate back to a URL containing a fragment, the router will first restore the saved scroll position, then scroll to the anchor if needed.
withInMemoryScrolling({
scrollPositionRestoration: 'enabled',
anchorScrolling: 'enabled'
});
Full Article & Demo
- Read the full article – a deep dive into the internals, edge cases, and performance considerations.
- Explore the live demo – see the feature in action with different configuration combos.
- Access the complete source code – clone, experiment, and adapt to your own projects.
Full Article →
Live Demo →
GitHub Repository →
Join the Conversation
- Did this article spark new ideas or solve a real problem? Let me know!
- Already using this technique? Share your experience.
- Got questions, doubts, or your own twist on the approach? Drop them in the comments—let’s learn together!
Spread the Word
If this guide added value to your development journey:
- Share it with your team, tech friends, or community—you never know who might need it right now.
- Save it for future reference.
Happy coding! 🚀
Quick Reference & Resources
I regularly share hands‑on tutorials, clean‑code tips, scalable frontend architecture, and real‑world problem‑solving guides.
| Platform | Description |
|---|---|
| Let’s connect professionally | |
| 🎥 Threads | Short‑form frontend insights |
| 🐦 X (Twitter) | Developer banter + code snippets |
| 👥 BlueSky | Stay up‑to‑date on frontend trends |
| 🌟 GitHub Projects | Explore code in action |
| 🌐 Website | Everything in one place |
| 📚 Medium Blog | Long‑form content and deep‑dives |
| 💬 Dev Blog | Free long‑form content and deep‑dives |
| ✉️ Substack | Weekly frontend stories & curated resources |
| 🧩 Portfolio | Projects, talks, and recognitions |
| ✍️ Hashnode | Developer blog posts & tech discussions |
| Developer blog posts & tech discussions |
🎉 If you found this article valuable
- Leave a 👏 Clap
- Drop a 💬 Comment
- Hit 🔔 Follow for more weekly frontend insights
Let’s build cleaner, faster, and smarter web apps — together.
Stay tuned for more Angular tips, patterns, and performance tricks! 🧪🧠🚀
✨ Share Your Thoughts → 📣 Set Your Notification Preference