docs/2.directory-structure/1.app/1.layouts.md
::tip{icon="i-lucide-rocket" } For best performance, components placed in this directory will be automatically loaded via asynchronous import when used. ::
Layouts are enabled by adding <NuxtLayout> to your app.vue:
<template>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template>
To use a layout:
layout property in your page with definePageMeta.name prop of <NuxtLayout>.appLayout property in route rules.::note
The layout name is normalized to kebab-case, so someLayout becomes some-layout.
::
::note
If no layout is specified, app/layouts/default.vue will be used.
::
::important
If you only have a single layout in your application, we recommend using app.vue instead.
::
::important
Unlike other components, your layouts must have a single root element to allow Nuxt to apply transitions between layout changes - and this root element cannot be a <slot />.
::
Add a ~/layouts/default.vue:
<template>
<div>
<p>Some default layout content shared across all pages</p>
<slot />
</div>
</template>
In a layout file, the content of the page will be displayed in the <slot /> component.
-| layouts/
---| default.vue
---| custom.vue
Then you can use the custom layout in your page:
<script setup lang="ts">
declare module 'nuxt/app' {
interface NuxtLayouts {
'custom': unknown
}
}
// ---cut---
definePageMeta({
layout: 'custom',
})
</script>
::read-more{to="/docs/4.x/directory-structure/app/pages#page-metadata"}
Learn more about definePageMeta.
::
You can directly override the default layout for all pages using the name property of <NuxtLayout>:
<script setup lang="ts">
// You might choose this based on an API call or logged-in status
const layout = 'custom'
</script>
<template>
<NuxtLayout :name="layout">
<NuxtPage />
</NuxtLayout>
</template>
If you have a layout in nested directories, the layout's name will be based on its own path directory and filename, with duplicate segments being removed.
| File | Layout Name |
|---|---|
~/layouts/desktop/default.vue | desktop-default |
~/layouts/desktop-base/base.vue | desktop-base |
~/layouts/desktop/index.vue | desktop |
For clarity, we recommend that the layout's filename matches its name:
| File | Layout Name |
|---|---|
~/layouts/desktop/DesktopDefault.vue | desktop-default |
~/layouts/desktop-base/DesktopBase.vue | desktop-base |
~/layouts/desktop/Desktop.vue | desktop |
:link-example{to="/docs/4.x/examples/features/layouts"}
You can also use the setPageLayout helper to change the layout dynamically:
<script setup lang="ts">
declare module 'nuxt/app' {
interface NuxtLayouts {
'custom': unknown
}
}
// ---cut---
function enableCustomLayout () {
setPageLayout('custom')
}
definePageMeta({
layout: false,
})
</script>
<template>
<div>
<button @click="enableCustomLayout">
Update layout
</button>
</div>
</template>
You can also set layouts for specific routes using the appLayout property in route rules:
export default defineNuxtConfig({
routeRules: {
// Set layout for specific route
'/admin': { appLayout: 'admin' },
// Set layout for multiple routes
'/dashboard/**': { appLayout: 'dashboard' },
// Disable layout for a route
'/landing': { appLayout: false },
},
})
::tip This is useful when you want to manage layouts centrally in your configuration rather than in each page file, or when you need to apply layouts to routes that don't have corresponding page components (such as catchall pages which might match many paths). ::
:link-example{to="/docs/4.x/examples/features/layouts"}
You can pass props to layouts in several ways.
definePageMetaUse the object syntax for the layout property to pass props directly from your page:
::code-group
<script setup lang="ts">
definePageMeta({
layout: {
name: 'panel',
props: {
sidebar: true,
title: 'Dashboard',
},
},
})
</script>
<script setup lang="ts">
const props = defineProps<{
sidebar?: boolean
title?: string
}>()
</script>
<template>
<div>
<aside v-if="sidebar">
Sidebar
</aside>
<main>
<h1>{{ title }}</h1>
<slot />
</main>
</div>
</template>
::
::tip
Props are fully typed based on your layout's defineProps. You'll get autocomplete and type-checking in your editor.
::
setPageLayoutYou can also pass props when changing the layout dynamically with setPageLayout:
setPageLayout('panel', { sidebar: true, title: 'Dashboard' })
If you are using pages, you can take full control by setting layout: false and then using the <NuxtLayout> component within the page.
::code-group
<script setup lang="ts">
definePageMeta({
layout: false,
})
</script>
<template>
<div>
<NuxtLayout name="custom">
<template #header>
Some header template content.
</template>
The rest of the page
</NuxtLayout>
</div>
</template>
<template>
<div>
<header>
<slot name="header">
Default header content
</slot>
</header>
<main>
<slot />
</main>
</div>
</template>
::
::important
If you use <NuxtLayout> within your pages, make sure it is not the root element (or disable layout/page transitions).
::