Fix: Project Has Layouts But NuxtLayout Not Used in Nuxt

Error message:
Your project has layouts but the `<NuxtLayout>` component has not been used.
layouts 2025-01-25

What Causes This Warning?

This warning appears when you have a layouts/ directory with layout files, but your app.vue (or entry point) doesn’t include the <NuxtLayout> component to render them.

The Problem

layouts/
  default.vue    ← Layout exists
  admin.vue      ← Layout exists
app.vue          ← But NuxtLayout isn't used
<!-- app.vue -->
<template>
  <!-- ❌ Missing NuxtLayout - layouts won't render -->
  <NuxtPage />
</template>

The Fix

Add NuxtLayout to app.vue

<!-- app.vue -->
<template>
  <!-- ✅ Wrap NuxtPage with NuxtLayout -->
  <NuxtLayout>
    <NuxtPage />
  </NuxtLayout>
</template>

Understanding the Layout System

Basic Setup

my-nuxt-app/
├── layouts/
│   └── default.vue    ← Applied automatically
├── pages/
│   └── index.vue
└── app.vue            ← Must include NuxtLayout
<!-- layouts/default.vue -->
<template>
  <div>
    <Header />
    <main>
      <slot />  <!-- Page content goes here -->
    </main>
    <Footer />
  </div>
</template>
<!-- app.vue -->
<template>
  <NuxtLayout>
    <NuxtPage />
  </NuxtLayout>
</template>

Multiple Layouts

<!-- layouts/default.vue -->
<template>
  <div class="default-layout">
    <Navbar />
    <slot />
    <Footer />
  </div>
</template>

<!-- layouts/admin.vue -->
<template>
  <div class="admin-layout">
    <AdminSidebar />
    <div class="admin-content">
      <slot />
    </div>
  </div>
</template>

<!-- layouts/blank.vue -->
<template>
  <div class="blank-layout">
    <slot />
  </div>
</template>

Using Layouts in Pages

<!-- pages/index.vue - uses default layout -->
<template>
  <div>Home page content</div>
</template>

<!-- pages/admin/index.vue - uses admin layout -->
<script setup>
definePageMeta({
  layout: 'admin'
})
</script>

<template>
  <div>Admin dashboard</div>
</template>

<!-- pages/login.vue - uses blank layout -->
<script setup>
definePageMeta({
  layout: 'blank'
})
</script>

<template>
  <div>Login form</div>
</template>

Alternative: No app.vue

If you don’t have an app.vue, Nuxt creates a default one. But if you create app.vue, you must include NuxtLayout:

<!-- Without app.vue - Nuxt handles it automatically -->

<!-- With app.vue - you must add NuxtLayout yourself -->
<template>
  <NuxtLayout>
    <NuxtPage />
  </NuxtLayout>
</template>

Disabling Layouts

If you don’t want layouts at all:

Option 1: Remove the layouts directory

rm -rf layouts/

Option 2: Disable for specific pages

<script setup>
definePageMeta({
  layout: false  // Disables layout for this page
})
</script>

Option 3: Keep app.vue without NuxtLayout

If you explicitly don’t want layout functionality:

<!-- app.vue -->
<template>
  <!-- No NuxtLayout - intentionally not using layouts -->
  <NuxtPage />
</template>

But then remove the layouts/ directory to avoid the warning.

Layout with Transitions

<!-- app.vue -->
<template>
  <NuxtLayout>
    <NuxtPage :transition="{ name: 'page', mode: 'out-in' }" />
  </NuxtLayout>
</template>

<style>
.page-enter-active,
.page-leave-active {
  transition: opacity 0.3s;
}
.page-enter-from,
.page-leave-to {
  opacity: 0;
}
</style>

Dynamic Layout Switching

<!-- app.vue -->
<script setup>
const route = useRoute()
const layout = computed(() => route.meta.layout || 'default')
</script>

<template>
  <NuxtLayout :name="layout">
    <NuxtPage />
  </NuxtLayout>
</template>

Layout with Error Handling

<!-- app.vue -->
<template>
  <NuxtLayout>
    <NuxtErrorBoundary>
      <NuxtPage />
      <template #error="{ error, clearError }">
        <ErrorPage :error="error" @clear="clearError" />
      </template>
    </NuxtErrorBoundary>
  </NuxtLayout>
</template>

Named Slots in Layouts

<!-- layouts/with-sidebar.vue -->
<template>
  <div class="layout">
    <aside>
      <slot name="sidebar">Default sidebar</slot>
    </aside>
    <main>
      <slot />  <!-- Default slot for page content -->
    </main>
  </div>
</template>

Usage in page:

<!-- pages/products.vue -->
<script setup>
definePageMeta({
  layout: 'with-sidebar'
})
</script>

<template>
  <div>
    <template #sidebar>
      <ProductFilters />
    </template>

    <!-- Main content goes in default slot -->
    <ProductList />
  </div>
</template>

Checking Current Layout

<script setup>
const layout = useLayout()
console.log('Current layout:', layout.value)

// Change layout programmatically
setPageLayout('admin')
</script>

Quick Checklist

  • app.vue includes <NuxtLayout> wrapping <NuxtPage>
  • Layout files are in layouts/ directory
  • Layout files use <slot /> for page content
  • Pages specify layout with definePageMeta({ layout: 'name' })
  • If not using layouts, remove layouts/ directory