Skip to content
Learni
View all tutorials
Frameworks Frontend

How to Start a Nuxt.js Project in 2026

Lire en français

Introduction

Nuxt.js is the meta-framework built on Vue.js that simplifies building universal web apps with server-side rendering (SSR), static site generation (SSG), and SPA capabilities. In 2026, Nuxt 3+ integrates Nitro for ultra-performant servers, automatic APIs, and native TypeScript support, making development faster and more scalable.

Why use it? It handles file-based routing, out-of-the-box SEO optimization, and one-click deployments to Vercel or Netlify. This beginner tutorial guides you step by step to create a complete app: installation, pages, components, navigation, and data fetching. By the end, you'll have a working project ready to bookmark. Ideal for beginner Vue developers ready to go pro with SSR. (128 words)

Prerequisites

  • Node.js 20+ installed
  • npm or yarn (npm recommended)
  • Basic HTML/JS knowledge (Vue.js optional)
  • Code editor like VS Code with Volar extension

Initialize the Nuxt Project

terminal
npx nuxi@latest init mon-app-nuxt
cd mon-app-nuxt
npm install
npm run dev

This command creates a new Nuxt 3 project with the default app/ structure. It installs dependencies and starts the dev server at http://localhost:3000. Avoid manually editing package.json to prevent breaking Nuxt scripts.

Understanding the Generated Structure

The project includes app.vue (root layout), pages/ for automatic routing, components/ for reusability, and nuxt.config.ts for configuration. Every .vue file in pages/ becomes a route. Open http://localhost:3000 to see the default home page.

Configure Nuxt with TypeScript

nuxt.config.ts
export default defineNuxtConfig({
  devtools: { enabled: true },
  modules: ['@nuxt/ui'],
  typescript: {
    strict: true
  },
  css: ['~/assets/css/main.css'],
  runtimeConfig: {
    public: {
      apiBase: process.env.API_BASE || 'https://api.example.com'
    }
  }
})

This config enables devtools, the Nuxt UI module, strict TypeScript, and global CSS. runtimeConfig handles secure environment variables. Install @nuxt/ui with npm install @nuxt/ui before running npm run dev again.

Create the Home Page Layout

app.vue
<template>
  <div>
    <NuxtPage />
    <UContainer>
      <h1>Bienvenue sur mon app Nuxt.js !</h1>
      <p>Cette app utilise le rendu SSR pour un SEO optimal.</p>
      <NuxtLink to="/about">Aller à About</NuxtLink>
    </UContainer>
  </div>
</template>

<script setup lang="ts">
// Composant racine avec slots automatiques
</script>

Replace the contents of app.vue with this global layout. renders child pages, comes from @nuxt/ui. handles client-side navigation without page refreshes. Test the link after saving.

Add Pages and Navigation

Now create routed pages. Use pages/index.vue for the home page and pages/about.vue for /about. Nuxt automatically generates the routes.

Dynamic Home Page

pages/index.vue
<template>
  <div>
    <h1>Accueil</h1>
    <p>Liste de tâches :</p>
    <ul>
      <li v-for="task in tasks" :key="task.id">{{ task.text }}</li>
    </ul>
    <button @click="addTask">Ajouter tâche</button>
  </div>
</template>

<script setup lang="ts">
const tasks = ref([
  { id: 1, text: 'Apprendre Nuxt' },
  { id: 2, text: 'Développer une app' }
])

const addTask = () => {
  tasks.value.push({ id: tasks.value.length + 1, text: 'Nouvelle tâche' })
}
</script>

This page uses Vue 3 Composition API's ref for reactive state. v-for loops through tasks, and @click handles the event. Copy-paste for an interactive home page without a backend.

About Page with Data Fetching

pages/about.vue
<template>
  <div>
    <h1>A propos</h1>
    <p>{{ message }}</p>
    <pre>{{ user }}</pre>
  </div>
</template>

<script setup lang="ts">
const { data: user } = await $fetch('/api/user')
const message = 'Page About chargée avec useFetch !'
</script>

useFetch (or $fetch in setup) loads async data at build or runtime. First create /server/api/user.ts for the API. This shows Nuxt's universal fetching (SSR/SSG).

Server API Route

server/api/user.ts
export default defineEventHandler(async (event) => {
  await new Promise(resolve => setTimeout(resolve, 1000))
  return {
    id: 1,
    name: 'John Doe',
    email: 'john@example.com'
  }
})

In server/api/, this Nitro route returns a simulated user with a delay to test loading states. defineEventHandler manages HTTP requests. Accessible at /api/user with SSR rendering.

Reusable Component

components/TaskList.vue
<template>
  <div>
    <h2>{{ title }}</h2>
    <ul>
      <li v-for="task in tasks" :key="task.id">
        {{ task.text }} <button @click="$emit('complete', task.id)">Terminé</button>
      </li>
    </ul>
  </div>
</template>

<script setup lang="ts">
defineProps<{
  title: string
  tasks: { id: number; text: string }[]
}>()

const emit = defineEmits<{
  (e: 'complete', id: number): void
}>()
</script>

Nuxt auto-imports this component. Typed TS props and emits enable parent-child communication. Use it in pages/index.vue like .

Best Practices

  • Use Composition API everywhere for optimal reactivity.
  • Enable strict TypeScript from the start to catch errors.
  • Prefer useFetch/useAsyncData for server vs. client data.
  • Organize into composables for reusable logic.
  • Set up runtimeConfig for secrets and env vars.

Common Errors to Avoid

  • Forgetting await on useFetch: causes SSR/client hydration mismatches.
  • Editing node_modules: use npm install for dependencies.
  • Skipping devtools: true: miss Nuxt/Vue inspections.
  • Manual routes: rely on file-based routing.

Next Steps

Master modules like @nuxt/content for headless CMS or @nuxt/image for optimization. Deploy to Vercel with npm run build && npm run preview. Check the official Nuxt docs and our Learni courses for advanced Vue/Nuxt training.