9. Лейауты и Outlet
Layouts в Remix — это компоненты, которые оборачивают дочерние маршруты. Они создают общую структуру страницы: навигацию, сайдбар, футер. Дочерний маршрут монтируется в <Outlet />.
Root Layout
Заголовок раздела «Root Layout»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 />;}Вложенные Layouts
Заголовок раздела «Вложенные Layouts»Каждый уровень маршрутизации может иметь свой layout:
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> );}Pathless Layouts
Заголовок раздела «Pathless Layouts»Если нужен layout без добавления сегмента в URL:
app/routes/ _marketing.tsx ← layout (нет в URL!) _marketing._index.tsx → / _marketing.about.tsx → /about _marketing.pricing.tsx → /pricingexport default function MarketingLayout() { return ( <div> <MarketingHeader /> {/* общая шапка */} <Outlet /> <MarketingFooter /> {/* общий футер */} </div> );}NavLink — активные ссылки
Заголовок раздела «NavLink — активные ссылки»<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>Loader в layout маршруте
Заголовок раздела «Loader в layout маршруте»Layout маршруты тоже могут иметь loader. Данные доступны всем дочерним маршрутам:
export async function loader({ request }) { const user = await requireUser(request); // защита всех /dashboard/* маршрутов const notifications = await getNotifications(user.id); return json({ user, notifications });}