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
npx nuxi@latest init mon-app-nuxt
cd mon-app-nuxt
npm install
npm run devThis 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
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
<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
<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
<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
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
<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
awaitonuseFetch: causes SSR/client hydration mismatches. - Editing
node_modules: usenpm installfor 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.