Frontend Academy · Lesson

useFetch and useAsyncData

Fetch server-side data with useFetch and useAsyncData, handle pending and error states, and understand how Nuxt hydrates the client.

Lesson 2 of 414 steps

useFetch and useAsyncData is a free Frontend Academy lesson on CoddyKit. This is lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Frontend Academy learning path, and your progress syncs across the web and the CoddyKit app. The Frontend Academy course includes 4 lessons in total.

Data Fetching in Nuxt 3

Nuxt provides two main composables for fetching data: useFetch (URL-based, calls fetch under the hood) and useAsyncData (any async function, with a unique key for caching/deduplication).

useFetch — The Quick Way

Pass a URL — Nuxt fetches it during SSR, hydrates on the client, and prevents the client from re-fetching on hydration.

<script setup>
const { data: posts, pending, error } = await useFetch('/api/posts');
</script>

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

Why No Double Fetch on Hydration?

Nuxt serialises the fetched data into the SSR HTML payload. On the client, useFetch hydrates from that payload — no second network call. This is the key SSR optimisation.

useAsyncData — Flexible Async

Use when fetching isn't a simple URL — DB queries, custom logic, multi-step requests.

<script setup>
const { data: user } = await useAsyncData('user-profile', async () => {
  const me = await $fetch('/api/me');
  const stats = await $fetch(`/api/users/${me.id}/stats`);
  return { ...me, stats };
});
</script>

Cache Key Matters

The first argument to useAsyncData is a unique cache key. Same key shared across components dedupes and re-uses the result. Different keys = independent fetches.

// Two components calling useFetch('/api/posts') share one request
// Two components calling useFetch('/api/posts', { key: 'a' }) and { key: 'b' } fetch twice

Refresh and Lazy

Both composables return refresh() to refetch and accept { lazy: true } to skip blocking SSR (data loads after the page renders).

const { data, refresh } = await useFetch('/api/posts');

async function onSave() {
  await $fetch('/api/posts', { method: 'POST', body: newPost });
  await refresh();
}

// Or lazy mode:
const { data, pending } = useFetch('/api/posts', { lazy: true });

Watch and Reactive URLs

Pass a function for the URL — it's reactive. Re-fetches when dependencies change.

<script setup>
const page = ref(1);
const { data } = await useFetch(() => `/api/posts?page=${page.value}`);
// Changing page.value re-runs the fetch
</script>

Request Options

useFetch accepts the same options as fetch — method, body, headers, plus Nuxt-specific options like default and transform.

const { data } = await useFetch('/api/posts', {
  method: 'POST',
  body: { title: 'Hello' },
  headers: { 'X-Custom': '1' },
  transform: (raw) => raw.items.map(transformItem),
  default: () => []
});

$fetch — Imperative Fetch

$fetch is Nuxt's universal fetcher (ofetch under the hood). No automatic hydration tracking. Use in event handlers and mutations.

async function deletePost(id) {
  await $fetch(`/api/posts/${id}`, { method: 'DELETE' });
  await refresh();
}

Error Handling

Throw an error inside the fetch handler to surface it. Pages can show a custom error page with ~/error.vue.

// error.vue at project root
<script setup>
const props = defineProps<{ error: any }>();
</script>

<template>
  <div>
    <h1>{{ error.statusCode }}</h1>
    <p>{{ error.message }}</p>
    <button @click="clearError({ redirect: '/' })">Home</button>
  </div>
</template>

Auth and Cookies

useFetch automatically forwards cookies during SSR, so authenticated requests work both on server and client without manual cookie handling.

useFetch vs useAsyncData — When?

useFetch: simple URL-based requests (the 95% case). useAsyncData: custom async logic, multi-step composition, non-URL sources (database via server/utils).

Quick Check

Why does useFetch in Nuxt 3 prevent a duplicate network request when the page hydrates on the client?

Recap: useFetch & useAsyncData

useFetch: URL-based, the 95% case. useAsyncData: custom async logic with a unique cache key. Both hydrate from SSR payload (no double request). refresh() to refetch, lazy:true to skip blocking SSR. Reactive URLs trigger refetch. $fetch for imperative mutations. error.vue handles errors. Cookies forwarded automatically during SSR.

Free to start

Learn HTML with an AI tutor — free

Write and run real code in your browser, get instant help from a 24/7 AI tutor, and pick up where you left off on the web or in the app.

Courses
41
Lessons
163

Frequently Asked Questions

Is the “useFetch and useAsyncData” lesson free?

Yes — the full text of “useFetch and useAsyncData” is free to read here on the web. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Frontend Academy course, upgrade to CoddyKit PRO. The Frontend Academy course includes 4 lessons in total.

What will I learn in “useFetch and useAsyncData”?

Fetch server-side data with useFetch and useAsyncData, handle pending and error states, and understand how Nuxt hydrates the client. You practise Frontend Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Frontend Academy?

No prior experience is required. Frontend Academy on CoddyKit is structured for beginners through advanced learners, so you can start here or from the beginning and move at your own pace. This is lesson 2 of 4.

How long does the “useFetch and useAsyncData” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Frontend Academy lesson?

Yes. Every Frontend Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. File-based Routing and Auto-imports
  2. useFetch and useAsyncData
  3. Nuxt Modules: Image Auth i18n
  4. Deployment: Static vs SSR
← Back to Frontend Academy