Layouts в TanStack Router представляют собой мощный инструмент для организации интерфейса приложения и повторного использования компонентов между различными маршрутами. Основная идея layouts — это возможность определять общий каркас страницы, который затем будет использоваться несколькими маршрутами, снижая дублирование кода и упрощая поддержку.
Layout в TanStack Router создаётся аналогично обычному маршруту, но с возможностью вложенных маршрутов:
import { createRouter, createRoute } from '@tanstack/router';
import { LayoutComponent } from './components/LayoutComponent';
import HomePage from './pages/HomePage';
import AboutPage from './pages/AboutPage';
const rootRoute = createRoute({
path: '/',
component: LayoutComponent,
children: [
createRoute({
path: 'home',
component: HomePage,
}),
createRoute({
path: 'about',
component: AboutPage,
}),
],
});
const router = createRouter({
route: rootRoute,
});
В данном примере LayoutComponent является общим каркасом
для маршрутов home и about. Любые элементы,
включённые в LayoutComponent, будут отображаться на всех
дочерних страницах.
Layout-компонент обычно содержит следующие элементы:
Header)Navigation)Footer)Outlet)Пример реализации Layout-компонента:
import { Outlet } from '@tanstack/router';
export default function LayoutComponent() {
return (
<div>
<header>
<h1>Моё приложение</h1>
<nav>
<a href="/home">Главная</a>
<a href="/about">О нас</a>
</nav>
</header>
<main>
<Outlet />
</main>
<footer>
© 2026 Компания
</footer>
</div>
);
}
Ключевой момент: <Outlet />
отвечает за рендеринг текущего дочернего маршрута. Без него вложенные
страницы не будут отображаться внутри layout.
Layouts особенно полезны, когда требуется одинаковая структура для нескольких маршрутов. Например, если есть несколько разделов приложения с одинаковым хедером и навигацией, можно создать один основной Layout и подключать его ко всем маршрутам.
Можно создавать вложенные layouts, чтобы ещё больше структурировать приложение. Например:
const dashboardLayout = createRoute({
path: '/dashboard',
component: DashboardLayout,
children: [
createRoute({
path: 'analytics',
component: AnalyticsPage,
}),
createRoute({
path: 'settings',
component: SettingsPage,
}),
],
});
В данном случае DashboardLayout может включать боковую
панель (sidebar), уникальную для раздела “dashboard”, но при этом
использовать общий <Header> и
<Footer> из основного layout.
TanStack Router позволяет подключать данные непосредственно на уровне layout. Это полезно, если layout должен отобразить информацию, загруженную до рендеринга дочерних маршрутов.
Пример с загрузкой данных:
const profileLayout = createRoute({
path: '/profile',
component: ProfileLayout,
loader: async () => {
const user = await fetch('/api/current-user').then(res => res.json());
return { user };
},
children: [
createRoute({
path: 'overview',
component: OverviewPage,
}),
createRoute({
path: 'settings',
component: ProfileSettingsPage,
}),
],
});
В ProfileLayout можно получить данные через хук
useLoaderData():
import { useLoaderData, Outlet } from '@tanstack/router';
export default function ProfileLayout() {
const { user } = useLoaderData();
return (
<div>
<header>
<h2>Добро пожаловать, {user.name}</h2>
</header>
<main>
<Outlet />
</main>
</div>
);
}
Таким образом, дочерние страницы автоматически получают доступ к контексту layout, а код становится более организованным и переиспользуемым.
Layouts также отлично подходят для управления состоянием и предоставления контекста дочерним маршрутам. Например, можно использовать React Context для хранения глобальных настроек или состояния авторизации:
import { createContext, useState } from 'react';
import { Outlet } from '@tanstack/router';
export const ThemeContext = createContext();
export default function AppLayout() {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<header>Моё приложение</header>
<main>
<Outlet />
</main>
</ThemeContext.Provider>
);
}
Все дочерние маршруты, рендерящиеся через
<Outlet />, смогут использовать
ThemeContext, не повторяя настройку состояния на каждой
странице.
Для крупных приложений можно подключать layout динамически с помощью React.lazy для оптимизации загрузки:
import { lazy } from 'react';
const LazyLayout = lazy(() => import('./LazyLayout'));
const lazyRoute = createRoute({
path: '/lazy',
component: LazyLayout,
children: [
createRoute({ path: 'page1', component: Page1 }),
],
});
Это снижает первоначальный размер бандла и ускоряет загрузку приложения.
Layouts делают структуру приложения модульной, логичной и лёгкой для поддержки. С их помощью разработка становится менее фрагментированной и позволяет сосредоточиться на функциональности отдельных компонентов, не дублируя общие элементы интерфейса.