Vercel Logo

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 uses named layouts and slots. Layouts live in app/layouts/, and every page uses default.vue unless it selects another one. Layout selection is explicit rather than inherited through route folders.

The starter includes a basic layout. You'll inspect its wiring and update the navigation for the pages built so far.

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:

app/app.vue
<template>
  <NuxtLayout>
    <NuxtPage />
  </NuxtLayout>
</template>

NuxtLayout selects the layout, defaulting to default.vue, and NuxtPage renders the matched page inside it. Next.js wires layouts implicitly through the folder tree. Nuxt opts into the layout system through app.vue, so removing NuxtLayout would render pages without a layout.

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

// 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:

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 matched page renders inside <slot />, which corresponds to {children} in a React layout. Vue's dedicated slot elements can also support multiple named insertion points, although this project only uses the default slot.

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.

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.

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

During navigation, the layout remains mounted while the content inside <slot /> changes.

Commit

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

app/app.vue
<template>
  <NuxtLayout>
    <NuxtPage />
  </NuxtLayout>
</template>
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>

Was this helpful?

supported.