The Data Fetching Story

useFetch on the Page, $fetch in Handlers

The Data Fetching Story

Nuxt gives you three pillars for fetching data — useFetch for pages, useAsyncData for custom logic, and $fetch for one-off calls inside handlers and actions.

4 min read Level 2/5 #nuxt#fetching#overview
What you'll learn
  • Recognize the three primitives Nuxt ships for data fetching
  • Pick the right one based on whether you are in a page or a handler
  • Know they share the same request cache and serialization pipeline

Most apps need to load data — from your own API, a third-party service, or a database. Nuxt unifies this story with three primitives that work the same way on the server during SSR and on the client after hydration.

The Three Primitives

  • useFetch(url) — the default page-level hook. Wraps useAsyncData plus $fetch.
  • useAsyncData(key, handler) — for custom logic, multiple fetches, transforms.
  • $fetch(url) — the universal HTTP client. Use it inside actions, handlers, callbacks.
<script setup lang="ts">
// Page-level — runs on server during SSR, hydrates on client.
const { data: posts } = await useFetch('/api/posts')

// Inside a click handler — server already rendered, just call $fetch.
async function createPost() {
  await $fetch('/api/posts', { method: 'POST', body: { title: 'Hi' } })
}
</script>

Server-First, No Double Fetch

useFetch and useAsyncData run on the server during SSR, serialize the result into the page payload, and the client hydrates from that payload — it does not re-fetch. This is the magic that makes Nuxt apps feel fast and SEO-friendly out of the box.

$fetch Is ofetch

$fetch is a thin wrapper over the ofetch library — it parses JSON, throws on non-2xx, and works in both Node and the browser. Reach for it inside:

  • Pinia store actions
  • Button click handlers
  • Server route handlers (server/api/*.ts)
  • Anywhere outside of setup() top-level
useFetch →