В TanStack Router модальные окна реализуются через отдельные маршруты, которые позволяют управлять состоянием модалки с помощью URL. Такой подход обеспечивает сохранение истории навигации, возможность прямых ссылок на модальные состояния и интеграцию с системой маршрутизации приложения.
Для создания модального окна сначала создаётся отдельный маршрут, который будет отвечать за отображение модалки. В отличие от обычных страниц, модальные маршруты обычно рендерятся поверх текущего контента.
import { createRouter, createRouteConfig } from '@tanstack/router';
import AppLayout from './AppLayout';
import HomePage from './HomePage';
import ModalContent from './ModalContent';
const rootRoute = createRouteConfig()
.createRoute({
path: '/',
component: AppLayout,
});
const homeRoute = rootRoute.createRoute({
path: '/',
component: HomePage,
});
const modalRoute = rootRoute.createRoute({
path: '/modal',
component: ModalContent,
modal: true, // специальное свойство, указывающее на модальный тип
});
Ключевой момент здесь — свойство modal: true. Оно
позволяет отличать обычные страницы от модальных окон при рендеринге и
управлении состоянием.
TanStack Router позволяет использовать состояние навигации для определения, какой маршрут рендерить как модальное окно. Для этого применяются два уровня рендеринга: базовый слой страницы и слой модалки.
import { RouterProvider, useRouter } from '@tanstack/router';
function App() {
const router = useRouter();
return (
<RouterProvider router={router}>
<div className="page-layer">
{router.renderMatch(homeRoute)}
</div>
{router.matches.some(match => match.route.modal) && (
<div className="modal-layer">
{router.renderMatches(router.matches.filter(match => match.route.modal))}
</div>
)}
</RouterProvider>
);
}
router.renderMatch() отвечает за рендеринг основной
страницы.router.renderMatches() рендерит все модальные маршруты,
соответствующие текущему URL.match.route.modal отделяет модальные маршруты
от обычных страниц.Обычная навигация для модальных окон строится так же, как и для страниц, но с учётом того, что открытие модалки не должно заменять текущую страницу:
import { useNavigate } from '@tanstack/router';
function HomePage() {
const navigate = useNavigate();
const openModal = () => {
navigate({ to: '/modal', modal: true });
};
return (
<div>
<h1>Главная страница</h1>
<button onCl ick={openModal}>Открыть модалку</button>
</div>
);
}
modal: true сообщает роутеру, что нужно
сохранить текущую страницу и открыть модалку поверх неё.navigate
с возвратом к предыдущему маршруту.function ModalContent() {
const navigate = useNavigate();
const closeModal = () => {
navigate({ to: '/' });
};
return (
<div className="modal">
<h2>Модальное окно</h2>
<button onCl ick={closeModal}>Закрыть</button>
</div>
);
}
Модальные окна через роутинг удобно параметризовать, передавая данные через URL или search-параметры:
const modalRoute = rootRoute.createRoute({
path: '/modal/:id',
component: ModalContent,
modal: true,
});
function ModalContent({ params }) {
return (
<div className="modal">
<h2>Модалка с ID: {params.id}</h2>
</div>
);
}
Такой подход позволяет создавать уникальные модальные состояния для каждой сущности, например карточки товара или профиля пользователя.
Для визуального отделения модалки от основной страницы часто
используют отдельный слой с анимацией. TanStack Router не ограничивает
использование CSS-анимаций или библиотек вроде Framer Motion, так как
рендеринг модалки полностью контролируется через компонент
renderMatches.
<div className="modal-layer">
{router.renderMatches(router.matches.filter(match => match.route.modal))}
</div>
Стили слоя могут включать затемнение фона и плавное появление:
.modal-layer {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
animation: fadeIn 0.3s ease-in-out;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
Использование модальных маршрутов позволяет:
modal: true.Такой подход делает модальные окна управляемыми через маршрутизацию и полностью совместимыми с современными SPA-приложениями на React и других совместимых фреймворках.