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