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
- Review
app/layouts/default.vueand understand the<slot />pattern - Add navigation links to all public pages
- 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:
- Review how
app.vueconnects to the layout system via<NuxtLayout>and<NuxtPage> - Update
app/layouts/default.vuewith a header containing the site title and navigation links - Include a
Browselink that points to/springs - Add a placeholder comment where auth links will go in Section 3
- 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.jslayout.tsx<NuxtLayout>inapp.vuetells 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:
<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:
// 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:
<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 — 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.
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.
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:
- Visit
http://localhost:3000. The header should show "Hot Springs Finder" and a "Browse" link - Click "Browse." The URL changes to
/springs, the page content swaps, but the header and footer stay put - Click "Hot Springs Finder" in the nav. You're back on the home page
- 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
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
<template>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template><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 — Built with Nuxt
</footer>
</div>
</template>Was this helpful?