---
title: "Project Setup"
description: "Clone the starter repo, install dependencies, and tour the Nuxt 4 project structure with a side-by-side comparison to Next.js conventions."
canonical_url: "https://vercel.com/academy/nuxt-on-vercel/nuxt-project-setup"
md_url: "https://vercel.com/academy/nuxt-on-vercel/nuxt-project-setup.md"
docset_id: "vercel-academy"
doc_version: "1.0"
last_updated: "2026-05-02T03:07:06.992Z"
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>

# Project Setup

# Set Up the Hot Springs Finder

Every framework has a shelving system. In Next.js, everything goes on one bookcase: pages, layouts, API routes, loading states, error boundaries, all in the `app/` directory. You know where to reach because you've memorized the system.

Nuxt shelves things differently. Client code on one shelf, server code on another. Layouts get their own section. Components auto-import themselves.

## Outcome

Scaffold a Nuxt 4 app with Tailwind CSS and understand how the project structure maps to Next.js.

## Fast Track

1. Clone the starter repo and install dependencies
2. Open the project and compare the file structure to Next.js
3. Start the dev server and confirm the home page loads

## Hands-on exercise 1.1

Clone the starter repo and get oriented in the Nuxt project structure.

**Requirements:**

1. Clone the starter repo and install dependencies with `pnpm install`
2. Review the file structure, paying attention to `app/`, `server/`, and `nuxt.config.ts`
3. Start the dev server with `pnpm dev` and visit `http://localhost:3000`
4. Read through `nuxt.config.ts` and identify what each option does

**Implementation hints:**

- The `app/` directory is Nuxt 4's way of separating your client-side code from server code. In Next.js, everything lives under `app/` too, but in Nuxt, `server/` is a completely separate directory with its own auto-imports
- `nuxt.config.ts` is the equivalent of `next.config.js`, but with a lot more built in. No need for separate Tailwind config files with v4
- Nuxt auto-imports Vue utilities (`ref`, `computed`, `watch`) and its own composables (`useFetch`, `useRoute`). No import statements needed for these

Here's how the project structure maps to what you already know:

```
Nuxt 4                          Next.js
─────                           ───────
app/                            app/
  pages/                          (route files)
  components/                     components/
  layouts/                        (layout.tsx files)
  composables/                    hooks/
  assets/                        assets/
/public                         public/ or styles/
server/                         app/api/
  api/                            (route handlers)
  routes/                         (no equivalent)
  data/                           (no equivalent)
nuxt.config.ts                  next.config.js
```

A few things that might trip you up:

- **`server/` is not inside `app/`.** In Next.js, your API routes live alongside your pages. In Nuxt, the server is a separate world powered by Nitro. It has its own auto-imports, its own utilities, and it doesn't know about Vue.
- **No `layout.tsx` files inside page folders.** Nuxt uses a dedicated `layouts/` directory. As long as `app.vue` wraps `<NuxtPage />` in `<NuxtLayout>`, `default.vue` is applied to every page automatically.
- **Auto-imports are aggressive.** Components in `components/` (including subfolders), composables in the top level of `composables/` (subfolders are not auto-imported), and all Vue utilities are available everywhere without importing them. This feels wrong at first. It's not.

Let's look at the config file that holds it all together:

```typescript title="nuxt.config.ts"
import tailwindcss from "@tailwindcss/vite";

export default defineNuxtConfig({
  compatibilityDate: "2025-05-01",

  css: ["~/assets/css/main.css"],

  vite: {
    plugins: [tailwindcss()],
  },
});
```

Note that the `~` in the CSS path is an alias for `app`.

Nuxt 4 uses the `app/` directory for client code and `server/` for server code by default. If you've seen older Nuxt 3 projects with everything at the project root, that's the old way. Nuxt 4 enforces the separation out of the box.

The `compatibilityDate` tells Nuxt which version of breaking-change behavior to use. Think of it like a snapshot: any breaking changes introduced after this date won't affect your app until you update the date.

Tailwind v4 doesn't need a config file. The `@tailwindcss/vite` plugin handles everything, and `main.css` just imports it:

```css title="app/assets/css/main.css"
@import "tailwindcss";
```

That's it. No `tailwind.config.js`, no `postcss.config.js`. If you've been maintaining those files in Next.js projects, this will feel suspiciously easy.

## Try It

Start the dev server:

```bash
pnpm dev
```

Visit `http://localhost:3000`. You should see the home page with "Find your next soak" and a "Browse Hot Springs" button.

Click "Browse" in the nav. You'll land on `/springs` with a placeholder message. That's expected. We haven't wired up data fetching yet.

\*\*Warning: If pnpm dev fails\*\*

Make sure you ran `pnpm install` first. If you see a Vue version mismatch error, delete `node_modules` and `pnpm-lock.yaml`, then run `pnpm install` again. Nuxt 4 requires Vue 3.5+.

\*\*Note: Port already in use?\*\*

If port 3000 is taken, Nuxt will automatically try 3001. Check your terminal output for the actual URL.

## Commit

```bash
git init && git add -A && git commit -m "feat(setup): scaffold Nuxt 4 app with Tailwind"
```

## Done-When

- [ ] `pnpm dev` starts without errors
- [ ] Home page loads at `http://localhost:3000` with the Hot Springs Finder heading
- [ ] You can navigate to `/springs` and see the placeholder page
- [ ] You can explain why Nuxt 4 separates `server/` from `app/`

## Solution

The starter repo already contains the complete setup for this lesson. Your project structure should look like this:

```
hot-springs-finder/
├── app/
│   ├── app.vue
│   ├── assets/css/main.css
│   ├── components/
│   ├── composables/
│   ├── layouts/
│   │   └── default.vue
│   └── pages/
│       ├── index.vue
│       ├── favorites.vue
│       ├── visited.vue
│       ├── login.vue
│       └── springs/
│           ├── index.vue
│           └── [id].vue
├── server/
│   ├── api/
│   └── data/
│       └── springs.json
├── public/
├── nuxt.config.ts
├── package.json
└── tsconfig.json
```

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

```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>
```


---

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