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

9. Лейауты и Outlet

Layouts в Remix — это компоненты, которые оборачивают дочерние маршруты. Они создают общую структуру страницы: навигацию, сайдбар, футер. Дочерний маршрут монтируется в <Outlet />.

root.tsx — главный layout всего приложения:

import {
Links, LiveReload, Meta, Outlet,
Scripts, ScrollRestoration
} from "@remix-run/react";
export function Layout({ children }: { children: React.ReactNode }) {
return (
<html lang="ru">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<Meta />
<Links />
</head>
<body>
{children}
<ScrollRestoration />
<Scripts />
</body>
</html>
);
}
export default function Root() {
return <Outlet />;
}

Каждый уровень маршрутизации может иметь свой layout:

app/routes/dashboard.tsx
import { Outlet, NavLink } from "@remix-run/react";
export default function DashboardLayout() {
return (
<div style={{ display: "grid", gridTemplateColumns: "250px 1fr" }}>
<aside>
<nav>
<NavLink to="/dashboard" end>Главная</NavLink>
<NavLink to="/dashboard/projects">Проекты</NavLink>
<NavLink to="/dashboard/team">Команда</NavLink>
</nav>
</aside>
<main>
<Outlet /> {/* дочерние страницы */}
</main>
</div>
);
}

Если нужен layout без добавления сегмента в URL:

app/routes/
_marketing.tsx ← layout (нет в URL!)
_marketing._index.tsx → /
_marketing.about.tsx → /about
_marketing.pricing.tsx → /pricing
_marketing.tsx
export default function MarketingLayout() {
return (
<div>
<MarketingHeader /> {/* общая шапка */}
<Outlet />
<MarketingFooter /> {/* общий футер */}
</div>
);
}
<NavLink
to="/dashboard/projects"
className={({ isActive, isPending }) =>
isActive ? "nav-link active" :
isPending ? "nav-link pending" : "nav-link"
}
style={({ isActive }) => ({
fontWeight: isActive ? "bold" : "normal",
color: isActive ? "#3992ff" : "#64748b",
})}
>
Проекты
</NavLink>

Layout маршруты тоже могут иметь loader. Данные доступны всем дочерним маршрутам:

dashboard.tsx
export async function loader({ request }) {
const user = await requireUser(request); // защита всех /dashboard/* маршрутов
const notifications = await getNotifications(user.id);
return json({ user, notifications });
}