Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/dvlkit/nuxe/llms.txt

Use this file to discover all available pages before exploring further.

Layouts let you share a consistent page shell — navigation, sidebars, footers — across many routes without duplicating markup. Nuxe discovers every .vue file in app/layouts/ at startup, registers each one by its lowercase filename, and automatically wraps each rendered page in the right layout. You never need to import or register a layout component yourself.

The default layout

layouts/default.vue is the fallback. Any page that does not declare a meta.layout in definePage is automatically wrapped with it. The layout receives the current page as its default slot.
app/layouts/default.vue
<template>
  <div>
    <header>
      <strong>Default Layout</strong>
    </header>
    <main>
      <slot />
    </main>
  </div>
</template>

Assigning a layout to a page

Call definePage inside <script setup> and pass the layout name as meta.layout. The name must match the filename of a file in app/layouts/ (without the .vue extension). definePage is auto-imported — no import statement needed.
app/pages/admin.vue
<script setup lang="ts">
definePage({
  meta: {
    layout: 'admin',
    middleware: 'admin'
  }
})
</script>

<template>
  <h1>Admin Page</h1>
  <p>This is rendered inside the Admin Layout</p>
</template>
When Nuxe resolves the layout for this route it reads route.meta.layout, lower-cases the value, and looks it up in the registered layout map — so 'admin' resolves to app/layouts/admin.vue.

Creating layouts

layouts/default.vue

The default layout used for all pages that do not specify one:
app/layouts/default.vue
<template>
  <div>
    <header>
      <strong>Default Layout</strong>
    </header>
    <main>
      <slot />
    </main>
  </div>
</template>

layouts/admin.vue

A custom layout for admin pages:
app/layouts/admin.vue
<template>
  <div>
    <header>
      <strong>Admin Layout</strong>
    </header>
    <main>
      <slot />
    </main>
  </div>
</template>

Layout name resolution is case-insensitive

Layout names are normalised to lowercase before the lookup. The values 'admin', 'Admin', and 'ADMIN' all resolve to app/layouts/admin.vue. This makes it safe to pass layout names from dynamic sources or CMS content without worrying about casing.
// All three are equivalent
definePage({ meta: { layout: 'admin' } })
definePage({ meta: { layout: 'Admin' } })
definePage({ meta: { layout: 'ADMIN' } })

Adding a new layout

To introduce a new layout, just create the file — no framework config change needed:
1

Create the layout file

Add app/layouts/blog.vue with a <slot /> where the page content should appear.
app/layouts/blog.vue
<template>
  <div>
    <aside>
      <!-- sidebar content -->
    </aside>
    <article>
      <slot />
    </article>
  </div>
</template>
2

Reference it in a page

Use definePage to assign the layout by name.
app/pages/blog/my-post.vue
<script setup lang="ts">
definePage({
  meta: { layout: 'blog' }
})
</script>

<template>
  <h1>My Post</h1>
</template>
Nuxe re-scans app/layouts/ whenever it starts (both in dev and build), so the new layout is picked up automatically.

Manual layout control with <NuxeLayout>

In most cases app/app.vue is the only place you need to reference the layout system — import NuxeLayout from @dvlkit/nuxe/components/nuxe-layout and use it to render the active layout around <RouterView />:
app/app.vue
<script setup lang="ts">
import { NuxeLayout } from '@dvlkit/nuxe/components/nuxe-layout'
</script>

<template>
  <NuxeLayout>
    <RouterView />
  </NuxeLayout>
</template>
If you need to override the layout programmatically, <NuxeLayout> accepts props:
PropTypeDescription
namestring | false | nullForce a specific layout name (overrides route.meta.layout). Pass false to render with no layout.
fallbackstring | nullLayout name to use when the resolved name is not found in the registry.
transitionboolean | objectWrap the layout in a <Transition>. Pass true for the default fade transition name, or an object of transition props.
app/app.vue
<template>
  <!-- Always use the admin layout, whatever the page says -->
  <NuxeLayout name="admin">
    <RouterView />
  </NuxeLayout>
</template>

Client-only layout content with <ClientOnly>

ClientOnly is auto-imported. It renders a comment node placeholder during SSR and mounts the real content after hydration on the client. Use it inside layouts to wrap anything that depends on browser-only APIs or third-party widgets.
app/layouts/default.vue
<template>
  <div>
    <header>
      <ClientOnly>
        <UserAvatarWidget />
        <template #fallback>
          <span>Loading…</span>
        </template>
      </ClientOnly>
    </header>
    <main>
      <slot />
    </main>
  </div>
</template>
<ClientOnly> supports a #fallback slot (or a #placeholder slot — both are equivalent) whose content is rendered on the server and swapped out after hydration.

Build docs developers (and LLMs) love