Scroll Behavior | Vue Router - Control Page Scrolling Like a Pro ๐ โ
Smart scrolling management - Create seamless navigation experiences with precise scroll control!
๐ฏ Overview โ
Scroll behavior configuration allows you to control how the page scrolls during navigation. Vue Router provides flexible options to customize scrolling behavior, from simple position restoration to complex scroll animations.
๐ Key Features โ
- Scroll Restoration: Automatically restore scroll position on back/forward navigation
- Custom Scroll Targets: Scroll to specific elements or positions
- Smooth Scrolling: Implement animated scroll transitions
- Conditional Scrolling: Control scrolling based on route changes
๐ก When to Configure Scroll Behavior โ
๐ Enhanced User Experience โ
Single-Page Applications
- Maintain scroll position during client-side navigation
- Implement smooth transitions between pages
- Handle anchor links and fragment navigation
Complex Layouts
- Scroll to specific sections within pages
- Handle fixed headers and navigation bars
- Manage scroll in multi-column layouts
๐ง Technical Scenarios โ
const router = createRouter({
scrollBehavior(to, from, savedPosition) {
// Custom scroll logic here
if (savedPosition) {
return savedPosition
} else if (to.hash) {
return { el: to.hash }
} else {
return { top: 0 }
}
}
})๐ Basic Configuration โ
Default Scroll Behavior โ
Vue Router's default behavior restores scroll position for back/forward navigation:
const router = createRouter({
history: createWebHistory(),
routes: [...],
scrollBehavior(to, from, savedPosition) {
// Default behavior: restore position if available, else scroll to top
if (savedPosition) {
return savedPosition
}
return { top: 0 }
}
})Scroll to Top on Navigation โ
Simple configuration to always scroll to top:
scrollBehavior(to, from, savedPosition) {
// Always scroll to top on navigation
return { top: 0 }
}๐ง Advanced Techniques โ
Example 1: Smart Scroll Restoration โ
scrollBehavior(to, from, savedPosition) {
// If it's a back/forward navigation, restore position
if (savedPosition) {
return savedPosition
}
// If route has hash, scroll to element
if (to.hash) {
return {
el: to.hash,
behavior: 'smooth', // Smooth scrolling
top: 20 // Offset for fixed header
}
}
// For new navigations, check if we should scroll to top
if (to.path !== from.path) {
return { top: 0 }
}
// Otherwise, maintain current position
return false
}Example 2: Route-Based Scroll Behavior โ
scrollBehavior(to, from, savedPosition) {
// Route-specific scroll behavior
const scrollConfig = {
// Default: scroll to top for new pages
default: { top: 0 },
// List pages: maintain scroll position
'/products': savedPosition || { top: 0 },
'/blog': savedPosition || { top: 0 },
// Detail pages: always start from top
'/products/:id': { top: 0 },
'/blog/:slug': { top: 0 },
// Admin area: different behavior
'/admin': { top: 0, behavior: 'instant' }
}
// Get configuration for current route
let config = scrollConfig.default
// Check for exact matches first
if (scrollConfig[to.path]) {
config = scrollConfig[to.path]
} else {
// Check for pattern matches
for (const pattern in scrollConfig) {
if (pattern.includes(':') && pathToRegexp(pattern).test(to.path)) {
config = scrollConfig[pattern]
break
}
}
}
return config
}๐ ๏ธ Practical Examples โ
Example 1: E-commerce Site Scroll Behavior โ
scrollBehavior(to, from, savedPosition) {
// Product listing - maintain scroll position
if (to.path === '/products' && from.path === '/products') {
// User is filtering/searching within products
return savedPosition || { top: 0 }
}
// Product detail - always start from top
if (to.path.startsWith('/products/') && to.params.id) {
return { top: 0, behavior: 'smooth' }
}
// Checkout process - no scrolling between steps
if (to.path.startsWith('/checkout') && from.path.startsWith('/checkout')) {
return false
}
// Back to product list from detail - try to restore position
if (from.path.startsWith('/products/') && to.path === '/products') {
return savedPosition || { top: 0 }
}
// Default: smooth scroll to top for page changes
if (to.path !== from.path) {
return { top: 0, behavior: 'smooth' }
}
return false
}Example 2: Documentation Site with Anchor Links โ
scrollBehavior(to, from, savedPosition) {
// Handle anchor links in documentation
if (to.hash) {
return {
el: to.hash,
behavior: 'smooth',
top: 80 // Offset for fixed header
}
}
// Between documentation pages, maintain reading position if possible
if (to.path.startsWith('/docs') && from.path.startsWith('/docs')) {
if (savedPosition) {
return savedPosition
}
// Try to find the main content area
const contentEl = document.querySelector('.content-main')
if (contentEl) {
return { el: contentEl, top: 0 }
}
}
// Default behavior
return savedPosition || { top: 0 }
}๐ Scroll Behavior Options โ
Available Configuration Properties โ
// Complete scroll configuration
{
el: '#target-element', // Scroll to specific element
top: 100, // Scroll to specific position (pixels)
left: 0, // Horizontal scroll position
behavior: 'smooth', // Scroll behavior: 'auto' or 'smooth'
offset: { top: 50 } // Additional offset
}
// Return false to prevent scrolling
return false
// Return a Promise for async operations
return new Promise(resolve => {
setTimeout(() => {
resolve({ top: 0 })
}, 500)
})Advanced Element Targeting โ
scrollBehavior(to, from, savedPosition) {
if (to.hash) {
return {
el: to.hash,
// Account for fixed header
top: document.querySelector('header')?.offsetHeight || 0,
behavior: 'smooth'
}
}
// Scroll to specific element based on route meta
if (to.meta.scrollTo) {
const targetEl = document.querySelector(to.meta.scrollTo)
if (targetEl) {
return {
el: targetEl,
behavior: 'smooth',
top: 20
}
}
}
return savedPosition || { top: 0 }
}๐ Performance Considerations โ
Debounced Scrolling โ
let scrollTimeout
scrollBehavior(to, from, savedPosition) {
// Clear any pending scroll operations
clearTimeout(scrollTimeout)
return new Promise(resolve => {
scrollTimeout = setTimeout(() => {
if (savedPosition) {
resolve(savedPosition)
} else if (to.hash) {
resolve({ el: to.hash, behavior: 'smooth' })
} else {
resolve({ top: 0 })
}
}, 100) // Small delay to ensure DOM is ready
})
}Conditional Smooth Scrolling โ
scrollBehavior(to, from, savedPosition) {
// Only use smooth scrolling for certain conditions
const useSmoothScrolling =
!to.meta?.instantScroll &&
!from.meta?.instantScroll &&
window.matchMedia('(prefers-reduced-motion: no-preference)').matches
const behavior = useSmoothScrolling ? 'smooth' : 'auto'
if (savedPosition) {
return { ...savedPosition, behavior }
}
if (to.hash) {
return { el: to.hash, behavior }
}
return { top: 0, behavior }
}๐ฏ Best Practices โ
โ Do โ
- Respect User Preferences: Check
prefers-reduced-motion - Handle Edge Cases: Account for element existence
- Use Appropriate Offsets: Consider fixed headers and navigation
- Test Thoroughly: Test on different devices and browsers
โ Don't โ
- Overuse Smooth Scrolling: Can be disorienting if overused
- Ignore Performance: Avoid complex calculations during navigation
- Forget Mobile: Test touch scrolling behavior
- Skip Error Handling: Handle missing elements gracefully
๐ Related Features โ
- Route Meta Fields: Store scroll configuration in route meta
- Navigation Guards: Combine with scroll behavior for complex logic
- Transition Components: Coordinate with page transitions
- Browser History API: Understand underlying browser behavior
๐ก Pro Tip
Combine scroll behavior with Vue's Intersection Observer API to create sophisticated scroll-based animations and lazy loading that work seamlessly with your routing!
Ready to master scroll control? Start implementing intelligent scroll behavior to create polished, professional navigation experiences! ๐