Layouts
Что такое layout
Layout — обёртка вокруг страницы. Header, Footer, Sidebar — общие для группы страниц.
Пример:
- Default layout — Header + Footer
- Auth layout — центрированная форма
- Admin layout — Sidebar + content
Создание
layouts/default.vue — применяется ко всем страницам по умолчанию:
<template>
<div class="app">
<AppHeader />
<main>
<slot />
</main>
<AppFooter />
</div>
</template>
<slot /> — место для страницы.
app.vue
<template>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template>
<NuxtLayout> — рендер выбранного layout.
Кастомный layout
layouts/auth.vue:
<template>
<div class="auth-layout">
<div class="auth-card">
<slot />
</div>
</div>
</template>
<style scoped>
.auth-layout {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: #f5f5f5;
}
.auth-card {
background: white;
padding: 40px;
border-radius: 12px;
min-width: 400px;
}
</style>
Использование
Через definePageMeta:
<script setup lang="ts">
definePageMeta({
layout: 'auth',
});
</script>
<template>
<form>...</form>
</template>
Динамически:
<script setup lang="ts">
const layout = 'admin';
</script>
<template>
<NuxtLayout :name="layout">
<NuxtPage />
</NuxtLayout>
</template>
Или через setPageLayout:
setPageLayout('admin');
Отключение layout
<script setup lang="ts">
definePageMeta({
layout: false,
});
</script>
Страница рендерится без обёртки.
Несколько слотов
layouts/admin.vue:
<template>
<div class="admin">
<aside>
<slot name="sidebar" />
</aside>
<main>
<slot />
</main>
</div>
</template>
Страница:
<template>
<template #sidebar>
<AdminMenu />
</template>
<h1>Dashboard</h1>
</template>
Layout для группы страниц
Группы () позволяют задать layout для нескольких страниц без URL:
pages/
├── (marketing)/
│ ├── index.vue
│ ├── pricing.vue
│ └── about.vue
└── (app)/
├── dashboard.vue
└── settings.vue
pages/(app).vue — определит layout для группы:
<script setup lang="ts">
definePageMeta({
layout: 'app',
});
</script>
Получение route в layout
<script setup lang="ts">
const route = useRoute();
</script>
<template>
<div>
<nav>
<NuxtLink
v-for="link in links"
:key="link.to"
:to="link.to"
:class="{ active: route.path === link.to }"
>
{{ link.label }}
</NuxtLink>
</nav>
<slot />
</div>
</template>
Переходы между layouts
<NuxtLayout name="default">
<NuxtPage />
</NuxtLayout>
Анимация:
<NuxtLayout>
<template #default>
<Transition name="fade" mode="out-in">
<NuxtPage />
</Transition>
</template>
</NuxtLayout>
Middleware для layouts
<script setup lang="ts">
definePageMeta({
layout: 'admin',
middleware: 'auth',
});
</script>
Практика — три layouts
layouts/default.vue:
<template>
<div class="min-h-screen flex flex-col">
<header>...</header>
<main class="flex-1">
<slot />
</main>
<footer>...</footer>
</div>
</template>
layouts/auth.vue:
<template>
<div class="min-h-screen grid place-items-center bg-gray-50">
<div class="w-full max-w-md bg-white rounded-lg shadow p-8">
<slot />
</div>
</div>
</template>
layouts/admin.vue:
<template>
<div class="min-h-screen grid grid-cols-[240px_1fr]">
<aside class="bg-gray-900 text-white p-6">
<AdminMenu />
</aside>
<main class="p-8">
<slot />
</main>
</div>
</template>
Итог
layouts/default.vue— по умолчаниюlayouts/xxx.vue— кастомныеdefinePageMeta({ layout: 'xxx' })— выборlayout: false— отключение<NuxtLayout>— в app.vue<slot />— место для страницы
Дальше — middleware.