0Pricing
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.

useFetch and useAsyncData is a free Frontend Academy lesson on CoddyKit — 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, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

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.

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, and the Frontend Academy course includes 4 lessons in total. 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.

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; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.

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