Nuxt.js
01 / 02

Routing, Layouts & Data Fetching

Routing, Layouts & Data Fetching

File-Based Routing & Layouts

<!-- pages/users/[id].vue — becomes route /users/:id -->
<script setup>
const route = useRoute()
const { data: user } = await useFetch(`/api/users/${route.params.id}`)

definePageMeta({
  layout: 'dashboard',
  middleware: 'auth',
})
</script>

<template>
  <div>{{ user.name }}</div>
</template>

<!-- layouts/dashboard.vue -->
<template>
  <div>
    <AppSidebar />
    <main><slot /></main>  <!-- page content renders here -->
  </div>
</template>

<!-- app.vue — the app's root -->
<template>
  <NuxtLayout>
    <NuxtPage />
  </NuxtLayout>
</template>

Data Fetching

<script setup>
// useFetch — SSR-aware: fetched on the server, the result is serialized
// into the payload and reused on the client, avoiding a duplicate request
const { data: posts, pending, error, refresh } = await useFetch('/api/posts')

// useAsyncData — for custom fetching logic beyond a simple URL
const { data: user } = await useAsyncData('user', () => $fetch(`/api/users/${id}`))

// useState — SSR-safe shared state, keyed per request (NOT a shared module variable,
// which would leak between different users' concurrent requests on the server)
const cartCount = useState('cart-count', () => 0)
</script>

<template>
  <div v-if="pending">Loading...</div>
  <div v-else-if="error">{{ error.message }}</div>
  <ul v-else><li v-for="post in posts" :key="post.id">{{ post.title }}</li></ul>
</template>

SEO with useHead

<script setup>
const { data: post } = await useFetch(`/api/posts/${route.params.slug}`)

useHead({
  title: post.value.title,
  meta: [
    { name: 'description', content: post.value.excerpt },
    { property: 'og:title', content: post.value.title },
  ],
})
</script>

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free