Перейти к содержимому

8. Вложенные маршруты

Вложенные маршруты — одна из самых мощных возможностей Remix. Они позволяют создавать сложные UI с несколькими уровнями layouts, при этом каждый уровень загружает свои данные параллельно.

// app/routes/dashboard.tsx — родительский маршрут
import { Outlet, NavLink } from "@remix-run/react";
export async function loader() {
// Данные для этого уровня
const user = await getCurrentUser();
return json({ user });
}
export default function Dashboard() {
const { user } = useLoaderData<typeof loader>();
return (
<div className="dashboard">
<nav>
<p>Привет, {user.name}!</p>
<NavLink to="/dashboard">Главная</NavLink>
<NavLink to="/dashboard/stats">Статистика</NavLink>
<NavLink to="/dashboard/settings">Настройки</NavLink>
</nav>
<main>
{/* Здесь рендерятся дочерние маршруты */}
<Outlet />
</main>
</div>
);
}
app/routes/
dashboard.tsx → /dashboard/* (layout)
dashboard._index.tsx → /dashboard
dashboard.stats.tsx → /dashboard/stats
dashboard.settings.tsx → /dashboard/settings
dashboard.users.$id.tsx → /dashboard/users/:id

Remix загружает данные всех вложенных маршрутов параллельно:

GET /dashboard/stats
Параллельно выполняются:
├── dashboard.tsx loader() → getCurrentUser()
└── dashboard.stats.tsx loader() → getStats()
Результат: самый медленный из двух определяет время загрузки
(а не их сумма, как было бы при последовательной загрузке!)

Через useRouteLoaderData:

// В дочернем маршруте dashboard.stats.tsx
import { useRouteLoaderData } from "@remix-run/react";
import type { loader as dashboardLoader } from "./dashboard";
export default function Stats() {
// Получаем данные из родительского loader
const dashData = useRouteLoaderData<typeof dashboardLoader>("routes/dashboard");
const { stats } = useLoaderData<typeof loader>();
return (
<div>
<h2>Привет, {dashData?.user.name}!</h2>
<p>Ваша статистика...</p>
</div>
);
}
// Родительский маршрут
export default function Parent() {
const data = useLoaderData();
return (
<div>
<Outlet context={{ theme: "dark", user: data.user }} />
</div>
);
}
// Дочерний маршрут
import { useOutletContext } from "@remix-run/react";
export default function Child() {
const { theme, user } = useOutletContext<{ theme: string; user: User }>();
return <p>Тема: {theme}, Пользователь: {user.name}</p>;
}