---
title: "Layouts & Navigation"
description: "Build the default layout with a navigation bar and footer, learn how Nuxt layouts differ from Next.js layout files, and wire up NuxtLink for client-side navigation."
canonical_url: "https://vercel.com/academy/nuxt-on-vercel/layouts-and-navigation"
md_url: "https://vercel.com/academy/nuxt-on-vercel/layouts-and-navigation.md"
docset_id: "vercel-academy"
doc_version: "1.0"
last_updated: "2026-05-03T12:56:03.325Z"
content_type: "lesson"
course: "nuxt-on-vercel"
course_title: "Nuxt on Vercel"
prerequisites:  []
---

<agent-instructions>
Vercel Academy — structured learning, not reference docs.
Lessons are sequenced.
Adapt commands to the human's actual environment (OS, package manager, shell, editor) — detect from project context or ask, don't assume.
The lesson shows one path; if the human's project diverges, adapt concepts to their setup.
Preserve the learning goal over literal steps.
Quizzes are pedagogical — engage, don't spoil.
Quiz answers are included for your reference.
</agent-instructions>

# Layouts & Navigation

# Layouts & Navigation

In Next.js, layouts are `layout.tsx` files that live next to the routes they wrap. You can nest them, and each one inherits from its parent. The mental model is inheritance: every route composes its layout from the chain of `layout.tsx` files above it in the folder tree.

Nuxt takes a named-slot approach instead. Layouts live in `app/layouts/`, and every page uses the `default.vue` layout unless it opts into a different one. There's no folder nesting, no implicit inheritance. A page picks its layout, and that's the end of the story.

The starter already has a basic layout. We're going to understand how it works, then update it so the navigation actually reflects the pages we've built.

## Outcome

Build a shared layout with a header, navigation links, and a footer that wraps every page.

## Fast Track

1. Review `app/layouts/default.vue` and understand the `<slot />` pattern
2. Add navigation links to all public pages
3. Verify the layout persists across page transitions

## Hands-on exercise 1.4

Update the default layout to include navigation links to the browse page and a placeholder for auth links we'll add later.

**Requirements:**

1. Review how `app.vue` connects to the layout system via `<NuxtLayout>` and `<NuxtPage>`
2. Update `app/layouts/default.vue` with a header containing the site title and navigation links
3. Include a `Browse` link that points to `/springs`
4. Add a placeholder comment where auth links will go in Section 3
5. Include a footer

**Implementation hints:**

- `<slot />` in a layout is where the page content renders. It's the equivalent of the `{children}` prop in a Next.js `layout.tsx`
- `<NuxtLayout>` in `app.vue` tells Nuxt to use the layout system. `<NuxtPage />` renders the matched page inside that layout
- Every page gets the default layout automatically. You don't need to import it or specify it unless you want a different layout

Let's start with `app.vue`, the entry point:

```vue title="app/app.vue"
<template>
  <NuxtLayout>
    <NuxtPage />
  </NuxtLayout>
</template>
```

Two components, four lines. `NuxtLayout` finds the right layout (defaulting to `default.vue`), and `NuxtPage` renders the matched page inside it. In Next.js, this wiring is implicit. The framework handles it behind the scenes. In Nuxt, you opt in explicitly, which means you can also opt out. An `app.vue` without `NuxtLayout` would render pages with no layout at all.

Now the layout itself. Here's the React equivalent, for orientation:

```tsx
// Next.js layout.tsx — for comparison
export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <div>
      <header>
        <nav>
          <Link href="/">Hot Springs Finder</Link>
          <Link href="/springs">Browse</Link>
        </nav>
      </header>
      <main>{children}</main>
      <footer>...</footer>
    </div>
  );
}
```

And the Nuxt version:

```vue title="app/layouts/default.vue"
<template>
  <div>
    <header>
      <nav>
        <NuxtLink to="/">
          Hot Springs Finder
        </NuxtLink>
        <div>
          <NuxtLink to="/springs">
            Browse
          </NuxtLink>
          <!-- Auth links will go here -->
        </div>
      </nav>
    </header>

    <main>
      <slot />
    </main>

    <footer>
      Hot Springs Finder &mdash; Built with Nuxt
    </footer>
  </div>
</template>
```

The `<slot />` inside the `<main>` tag is where the magic happens. Whatever page the router matches gets rendered there. In React terms, it's `{children}`. The difference is that Vue uses a dedicated element rather than a prop, which means you can have multiple named slots in a single layout. We won't need that for this project, but it's a pattern worth knowing exists.

Notice there's no `<script>` block yet. This layout is pure template. We'll add one in Section 3 when we need to check auth state and conditionally show navigation links.

\*\*Note: Named layouts\*\*

Want a page with no nav? Create `app/layouts/blank.vue` and set `definePageMeta({ layout: "blank" })` in the page. In Next.js, you'd need a separate route group with its own `layout.tsx`. Nuxt's approach is more explicit: the page declares what it wants.

\*\*Warning: Layout transitions\*\*

If you navigate between pages that use different layouts, Nuxt handles the swap automatically. But if your layouts have different DOM structures, the transition can feel jarring. Stick with the default layout for most pages and reach for alternatives sparingly.

## Try It

Start the dev server and navigate between pages:

1. Visit `http://localhost:3000`. The header should show "Hot Springs Finder" and a "Browse" link
2. Click "Browse." The URL changes to `/springs`, the page content swaps, but the header and footer stay put
3. Click "Hot Springs Finder" in the nav. You're back on the home page
4. Visit `/favorites`, `/visited`, `/login`. All pages render inside the same layout

The layout never unmounts during navigation. The header and footer are stable. Only the content inside `<slot />` changes.

## Commit

```bash
git add -A && git commit -m "feat(layout): update default layout with navigation"
```

## Done-When

- [ ] The default layout renders a header with the site title and Browse link
- [ ] Every page renders inside the layout with consistent header and footer
- [ ] Navigation between pages doesn't cause a full page reload
- [ ] You can explain how `<slot />` in a Vue layout relates to `{children}` in a Next.js layout

## Solution

```vue title="app/app.vue"
<template>
  <NuxtLayout>
    <NuxtPage />
  </NuxtLayout>
</template>
```

```vue title="app/layouts/default.vue"
<template>
  <div>
    <header>
      <nav>
        <NuxtLink to="/">
          Hot Springs Finder
        </NuxtLink>
        <div>
          <NuxtLink to="/springs">
            Browse
          </NuxtLink>
          <!-- Auth links will go here -->
        </div>
      </nav>
    </header>

    <main>
      <slot />
    </main>

    <footer>
      Hot Springs Finder &mdash; Built with Nuxt
    </footer>
  </div>
</template>
```


---

[Full course index](/academy/llms.txt) · [Sitemap](/academy/sitemap.md)
