Модальные окна через роутинг

В 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; }
}

Сохранение истории и прямые ссылки

Использование модальных маршрутов позволяет:

  • Использовать кнопки “Назад” и “Вперед” браузера для закрытия и открытия модалки.
  • Создавать прямые ссылки на модальные состояния, что удобно для социальных сетей или email-рассылок.
  • Хранить состояние модалки через query-параметры, если необходимо передавать дополнительные данные.

Итоговые особенности

  • Модальные окна реализуются как маршруты с флагом modal: true.
  • Основной контент и модалки рендерятся в отдельных слоях.
  • Навигация через TanStack Router обеспечивает историю браузера и прямые ссылки.
  • Поддерживаются параметры URL для уникальных модальных состояний.
  • Можно легко интегрировать анимацию и кастомные стили.

Такой подход делает модальные окна управляемыми через маршрутизацию и полностью совместимыми с современными SPA-приложениями на React и других совместимых фреймворках.