---
title: "Pages & Routing"
description: "Create page files for the Hot Springs Finder, learn how Nuxt's file-based routing compares to Next.js, and navigate between pages with NuxtLink."
canonical_url: "https://vercel.com/academy/nuxt-on-vercel/pages-and-routing"
md_url: "https://vercel.com/academy/nuxt-on-vercel/pages-and-routing.md"
docset_id: "vercel-academy"
doc_version: "1.0"
last_updated: "2026-10-16T17:45:49.934Z"
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>

# Pages & Routing

# Pages & Routing

In Next.js, a route is a file called `page.tsx` inside a folder that matches the URL. Want `/about`? Create `app/about/page.tsx`. Want `/springs/[id]`? Create `app/springs/[id]/page.tsx`. The folder is the route, the file is always `page.tsx`.

In Nuxt, the file defines the route. `/about` comes from `app/pages/about.vue`, with no wrapper folder or required `page` filename. The structure is compact once you know the naming rules.

You'll build the Hot Springs Finder pages and compare both routing conventions.

## Outcome

Create the route structure for the Hot Springs Finder with home, browse, and detail pages.

## Fast Track

1. Create page files in `app/pages/` for each route
2. Add `NuxtLink` navigation between pages
3. Verify routes resolve correctly in the browser

## Hands-on exercise 1.2

Build the page structure for the app. Right now, the starter has placeholder pages. We'll update them with real structure and connect them with navigation.

**Requirements:**

1. Verify these pages exist in `app/pages/` and understand which URL each one maps to
2. Update the home page with a link to the browse page using `NuxtLink`
3. Navigate between pages and confirm the routes work

**Implementation hints:**

- `NuxtLink` is the equivalent of Next.js's `Link` component. It's auto-imported, so you don't need to import it
- In Nuxt, `pages/springs/index.vue` maps to `/springs` and `pages/springs/[id].vue` maps to `/springs/:id`. In Next.js, those would be `app/springs/page.tsx` and `app/springs/[id]/page.tsx`
- Square brackets for dynamic segments work the same way conceptually, but the file structure is flatter

Here's the side-by-side for every route in our app:

```
URL              Nuxt file                      Next.js file
───              ─────────                      ────────────
/                pages/index.vue                app/page.tsx
/springs         pages/springs/index.vue        app/springs/page.tsx
/springs/:id     pages/springs/[id].vue         app/springs/[id]/page.tsx
/favorites       pages/favorites.vue            app/favorites/page.tsx
/visited         pages/visited.vue              app/visited/page.tsx
/login           pages/login.vue                app/login/page.tsx
```

Notice the pattern. Next.js always needs a folder. Nuxt lets you use either a folder with `index.vue` or a standalone file. `/favorites` doesn't need a `favorites/` folder because there are no nested routes under it. `/springs` uses a folder because it has a child route (`[id]`).

Now let's look at how navigation works. In Next.js, you'd write:

```tsx
import Link from "next/link";

<Link href="/springs">Browse</Link>
```

In Nuxt, `NuxtLink` is auto-imported. It accepts `href`, but `to` is the convention across Vue Router and most Vue UI libraries. We'll use `to`:

```vue
<NuxtLink to="/springs">Browse</NuxtLink>
```

The home page already uses this pattern. Let's look at the link that takes users from the home page to the browse page:

```vue title="app/pages/index.vue" {12-17}
<template>
  <div>
    <section>
      <h1>
        Find your next soak
      </h1>
      <p>
        Discover hot springs around the world. Browse by region, temperature, or
        type. Save your favorites and track the ones you've visited.
      </p>
      <div>
        <NuxtLink to="/springs">
          Browse Hot Springs
        </NuxtLink>
      </div>
    </section>
  </div>
</template>
```

`NuxtLink` handles client-side navigation and prefetches the target route's code when the link enters the viewport.

One thing that might catch you off guard: `useRoute()` in Nuxt returns a reactive route object, similar to Next.js's `useParams()` and `useSearchParams()` combined into one. We'll use this heavily when we build the detail page in a later lesson.

```vue
<script setup lang="ts">
const route = useRoute();
// route.params.id: same as useParams() in Next.js
// route.query.search: same as useSearchParams().get("search")
</script>
```

No import needed. `useRoute` is auto-imported like everything else in Nuxt.

\*\*Note: Navigating in Nuxt\*\*

`useRoute()` gives you the current route as a read-only object. For the equivalent of Next.js's `useRouter().push()`, prefer `navigateTo("/springs")`; it works on the server and supports redirects. Use `useRouter()` when you need the raw router instance.

\*\*Warning: Catch-all routes look different\*\*

Next.js uses `[...slug]/page.tsx` for a catch-all route, while Nuxt uses `[...slug].vue`. This project does not need one, but the syntax is useful when translating routes.

## Try It

Start the dev server if it's not already running:

```bash
pnpm dev
```

1. Visit `http://localhost:3000`. Click "Browse Hot Springs." You should land on `/springs`
2. Check the URL bar. Click the browser back button. You should return to `/` without a full page reload
3. Visit `http://localhost:3000/springs/breitenbush-hot-springs` directly. You should see the detail page placeholder
4. Visit `http://localhost:3000/favorites` and `http://localhost:3000/visited`. Both should render their placeholder content

All six routes should resolve without 404 errors.

## Commit

```bash
git add -A && git commit -m "feat(routing): verify page routes and NuxtLink navigation"
```

## Done-When

- [ ] All six routes (`/`, `/springs`, `/springs/:id`, `/favorites`, `/visited`, `/login`) render without errors
- [ ] Clicking `NuxtLink` navigates without full page reloads
- [ ] You can explain when to use `useRoute()`, `navigateTo()`, and `useRouter()` in Nuxt
- [ ] You can map any Next.js route file to its Nuxt equivalent

## Solution

The starter already contains all the page files. The key files for routing are:

```
app/pages/
├── index.vue              → /
├── favorites.vue          → /favorites
├── visited.vue            → /visited
├── login.vue              → /login
└── springs/
    ├── index.vue          → /springs
    └── [id].vue           → /springs/:id
```

```vue title="app/pages/index.vue"
<template>
  <div>
    <section>
      <h1>
        Find your next soak
      </h1>
      <p>
        Discover hot springs around the world. Browse by region, temperature, or
        type. Save your favorites and track the ones you've visited.
      </p>
      <div>
        <NuxtLink to="/springs">
          Browse Hot Springs
        </NuxtLink>
      </div>
    </section>
  </div>
</template>
```

```vue title="app/pages/springs/index.vue"
<template>
  <div>
    <h1>
      Browse Hot Springs
    </h1>
    <p>
      Hot springs will be listed here once we wire up data fetching.
    </p>
  </div>
</template>
```

```vue title="app/pages/springs/[id].vue"
<template>
  <div>
    <h1>
      Spring Detail
    </h1>
    <p>
      Individual spring details will appear here once we build dynamic routes.
    </p>
  </div>
</template>
```


---

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