При построении одностраничных приложений (SPA) часто возникает необходимость контролировать поведение скролла при навигации между страницами. В отличие от классических многостраничных сайтов, где браузер автоматически прокручивает страницу вверх при переходе, в React-приложениях навигация через React Router не изменяет позицию скролла по умолчанию. Для корректного UX необходимо реализовать собственное управление скроллом.
useEffect для сброса скроллаОдним из простых способов является использование хука
useEffect вместе с useLocation из
react-router-dom. useLocation предоставляет
текущий объект маршрута, и при его изменении можно программно изменять
позицию скролла:
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
function ScrollToTop() {
const { pathname } = useLocation();
useEffect(() => {
window.scrollTo(0, 0);
}, [pathname]);
return null;
}
export default ScrollToTop;
window.scrollTo(0, 0) — мгновенно
прокручивает страницу в верх.pathname,
обеспечивая корректное поведение при переходе между маршрутами.ScrollToTop обычно размещают в корне
приложения, рядом с <Routes>.Для улучшения UX можно добавить плавное движение скролла:
useEffect(() => {
window.scrollTo({
top: 0,
beh * avior: 'smooth'
});
}, [pathname]);
beh * avior: 'smooth' делает переход более
естественным.В сложных приложениях важно не только сбрасывать скролл, но и восстанавливать его при возврате на предыдущий маршрут. React Router не делает этого автоматически. Для реализации необходимо хранить позиции скролла:
import { useEffect, useRef } from 'react';
import { useLocation } from 'react-router-dom';
function ScrollMemory() {
const { pathname } = useLocation();
const positions = useRef({});
useEffect(() => {
const savedPosition = positions.current[pathname];
if (savedPosition) {
window.scrollTo(savedPosition.x, savedPosition.y);
} else {
window.scrollTo(0, 0);
}
return () => {
positions.current[pathname] = {
x: window.scrollX,
y: window.scrollY
};
};
}, [pathname]);
return null;
}
positions.current хранит координаты для каждого
маршрута.Иногда требуется разное поведение для разных маршрутов. Для этого можно передавать параметры или использовать условные конструкции:
useEffect(() => {
if (pathname === '/long-page') {
window.scrollTo({ top: 500, beh * avior: 'smooth' });
} else {
window.scrollTo({ top: 0, beh * avior: 'auto' });
}
}, [pathname]);
Для более сложных сценариев можно использовать специализированные пакеты, например react-router-scroll-restoration или react-router-scroll-top, которые автоматизируют сохранение и восстановление позиции скролла. Основные преимущества:
Пример интеграции:
import ScrollRestoration from 'react-router-scroll-restoration';
function App() {
return (
<>
<ScrollRestoration />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</>
);
}
<ScrollRestoration /> отслеживает
маршруты и сохраняет позиции скролла автоматически.В некоторых случаях требуется прокрутка к конкретному элементу,
например при переходе к секции страницы. Это реализуется через рефы и
scrollIntoView:
import { useRef, useEffect } from 'react';
import { useLocation } from 'react-router-dom';
function SectionScroll() {
const sectionRef = useRef(null);
const { hash } = useLocation();
useEffect(() => {
if (hash && sectionRef.current) {
sectionRef.current.scrollIntoView({ beh * avior: 'smooth' });
}
}, [hash]);
return <div id="target-section" ref={sectionRef}>Контент секции</div>;
}
#section) для
прокрутки.scrollIntoView({ beh * avior: 'smooth' }) обеспечивает
плавную анимацию.window.scrollTo на больших
страницах.Контроль скролла в React Router требует аккуратного подхода: важно различать простые случаи (сброс к верху) и сложные сценарии (сохранение позиции, прокрутка к элементам). Сочетание хуков, рефов и сторонних библиотек позволяет реализовать плавный и предсказуемый UX для SPA-приложений.