Vue Framework

Nuxt

Nuxt is the Vue framework that adds file-based routing, auto-imports, server rendering and a built-in server engine, so a Vue app becomes a full-stack project.

intermediate14 min readUpdated Sep 15, 2026
users.vue
vue
<!-- pages/users.vue -->
<script setup>
const { data: users, pending, error } = await useFetch("/api/users");
</script>

<template>
  <p v-if="pending">Loading…</p>
  <p v-else-if="error">Something went wrong.</p>
  <ul v-else>
    <li v-for="user in users" :key="user.id">
      {{ user.name }}
    </li>
  </ul>
</template>
Built on
Vue 3
Router
File-based (pages/)
Server
Nitro engine
Data
useFetch, useAsyncData
Rendering
SSR, SSG, SPA, hybrid
Imports
Automatic

Why it matters

Why developers choose Nuxt

Automatic everything

Components, composables and Vue APIs are auto-imported, so you write less boilerplate and keep files focused.

A real server built in

The Nitro engine powers API routes, server middleware and multiple deployment targets from the same codebase.

Fast by default

Server rendering, route-level code splitting and payload extraction are configured for you out of the box.

The big picture

The three ideas behind Nuxt

A file-system router, automatic imports and the Nitro server engine working together in one project.

The file router

Routing

Files in pages/ become routes, with layouts, nested routes and route middleware.

Auto-imports

Ergonomics

Components, composables and utilities are available without import statements.

Nitro

Server

A server engine that handles API routes, rendering and deployment adapters.

Nuxt at a glance

What Nuxt adds to Vue

File-based routing

pages/ maps files to routes, including dynamic and nested segments.

Layouts

Share a shell across pages and switch layouts per route.

useFetch & useAsyncData

SSR-friendly data fetching with loading and error state built in.

Server routes

Write API endpoints in server/api with the Nitro engine.

SEO and meta

useHead and useSeoMeta manage titles, descriptions and social tags.

Modules

A rich module ecosystem adds features with a single line of config.

A short history

From Nuxt 1 to a modern Vue platform

  1. 2016

    Nuxt 1

    Nuxt brings server rendering and routing conventions to Vue applications.

    16
  2. 2018

    Nuxt 2

    A stable, widely adopted release with a large module ecosystem.

    18
  3. 2022

    Nuxt 3

    A rewrite on Vue 3 and Vite introduces the Nitro server and the Composition API.

    22
  4. 2024

    Nuxt 3 maturity

    Better performance, a unified server layer and first-class TypeScript.

    24
  5. Today

    The Vue meta-framework

    The default choice for full-stack and content-heavy Vue projects.

    Today

The complete guide

Nuxt: Everything you need to know

What is Nuxt?

Nuxt is the Vue meta-framework. Vue gives you components and reactivity; Nuxt adds the parts every real application needs — routing, server rendering, data fetching, a server runtime and a module ecosystem — and wires them together with sensible defaults.

It is the Vue counterpart to Next.js, and it follows the same broad philosophy: use the file system for routing, render on the server by default, and make data fetching a first-class concern. If you know Vue, Nuxt feels like a natural next step rather than a new framework.

File-based routing

Files in pages/ become routes. Dynamic segments use square brackets, and nested folders create nested routes.

pages/
├── index.vue            # /
├── about.vue            # /about
├── blog/
│   ├── index.vue        # /blog
│   └── [slug].vue       # /blog/:slug
└── dashboard/
    ├── index.vue        # /dashboard
    └── settings.vue     # /dashboard/settings
<!-- pages/blog/[slug].vue -->
<script setup>
const route = useRoute();
const { data: post } = await useFetch(`/api/posts/${route.params.slug}`);
</script>

<template>
  <article>
    <h1>{{ post.title }}</h1>
    <div v-html="post.body" />
  </article>
</template>

useRoute gives you the current route, and NuxtLink handles client-side navigation with prefetching. Layouts in layouts/ wrap pages and let you share a shell across many routes.

Auto-imports

Nuxt automatically imports Vue’s APIs, its own composables, and your components and composables. You write less boilerplate and files stay focused.

<!-- components/UserCard.vue -->
<script setup>
const props = defineProps({ user: Object });
</script>

<template>
  <article class="card">
    <h3>{{ user.name }}</h3>
  </article>
</template>

Components in components/ are available anywhere without importing them, and your own composables in composables/ are auto-imported too. The result is less ceremony and fewer import lists to maintain.

Data fetching with useFetch

useFetch and useAsyncData are the recommended way to load data. They run during server rendering, transfer the result to the client and expose loading and error state.

<!-- pages/posts.vue -->
<script setup>
const { data: posts, pending, error } = await useFetch("/api/posts");
</script>

<template>
  <p v-if="pending">Loading…</p>
  <p v-else-if="error">Could not load posts.</p>
  <PostList v-else :posts="posts" />
</template>

Because the request happens on the server, the first paint already includes the data. That avoids the flash of empty content you get from fetching in onMounted, and it works with caching, revalidation and custom keys for finer control.

The Nitro server

Nuxt includes a server engine called Nitro. Files in server/api/ become API endpoints, and server/routes/ can serve custom handlers.

// server/api/users.get.ts
export default defineEventHandler(async () => {
  const config = useRuntimeConfig();
  return $fetch("https://api.example.com/users", {
    headers: { Authorization: config.apiKey },
  });
});
// server/api/users.post.ts
export default defineEventHandler(async (event) => {
  const body = await readBody(event);
  const user = await db.user.create({ data: body });
  return { user };
});

This keeps secrets and third-party credentials on the server. The client calls your own endpoint, and Nitro handles the rest. The same engine deploys to Node, serverless functions, edge runtimes and static hosts through adapters.

SEO, meta and modules

Nuxt makes server-friendly SEO straightforward with composables.

<!-- pages/about.vue -->
<script setup>
useSeoMeta({
  title: "About us",
  description: "Who we are and what we build.",
  ogImage: "/og/about.png",
});
</script>

The useHead and useSeoMeta composables manage titles, descriptions, canonical URLs and social tags, and they render correctly on the server so crawlers see them. Beyond that, the Nuxt module ecosystem adds authentication, content, images, analytics and more with a line or two of config.

Rendering modes

Nuxt supports several rendering strategies and lets you choose per route:

  • Universal (SSR) renders on the server and hydrates on the client — the default.
  • Static (SSG) prerenders pages at build time.
  • SPA renders entirely on the client, like a classic Vue app.
  • Hybrid mixes them, so marketing pages can be static while a dashboard is client-rendered.

You can also enable incremental static regeneration and route rules to fine-tune caching and rendering without restructuring the app.

Best practices

  • Use useFetch and useAsyncData instead of fetching in onMounted.
  • Keep secrets and third-party calls in server/api routes.
  • Let auto-imports do their job; do not fight them with manual imports.
  • Define SEO metadata with useSeoMeta on every page.
  • Choose a rendering mode per route rather than one mode for everything.
  • Use NuxtLink for internal navigation so prefetching and transitions work.
  • Organise reusable logic into composables in composables/.

Common mistakes

  • Fetching data in onMounted and losing the SSR benefit.
  • Exposing API keys by calling third parties directly from the client.
  • Reaching for a global store when useState or a composable would do.
  • Ignoring route rules and over-rendering pages that could be static.
  • Assuming Nuxt 2 module APIs work unchanged in Nuxt 3.
  • Skipping SEO metadata because the framework makes it easy to forget.

Where to go next

Nuxt turns Vue into a full-stack platform. Deepen your Vue knowledge, add TypeScript, and compare the approach with Next.js and SvelteKit. Then build a small app with a page, a server route and useFetch to feel how naturally the pieces fit.

Fetching data

useFetch runs on the server during SSR and hydrates the result, so there is no flash and no manual state.

Prefer
<script setup>
const { data: posts } = await useFetch("/api/posts");
</script>

<template>
  <PostList :posts="posts" />
</template>
Avoid
<script setup>
const posts = ref([]);
onMounted(async () => {
  posts.value = await fetch("/api/posts")
    .then((r) => r.json());
});
</script>

Server logic

Keep secrets and third-party calls on the Nitro server, and let the client call your own endpoint.

Prefer
// server/api/users.get.ts
export default defineEventHandler(async () => {
  const key = useRuntimeConfig().apiKey;
  return $fetch("https://api.example.com/users", {
    headers: { Authorization: key },
  });
});
Avoid
// calling a third party directly
// from the client exposes the key
const data = await $fetch(
  "https://api.example.com/users",
  { headers: { Authorization: apiKey } },
);

FAQ

Frequently asked questions

Keep learning

Related topics from the roadmap.

$ start learning

Ready to start learning Nuxt?

Our interactive tutorial walks you through Nuxt step by step — with quizzes and real code you can run in the browser.