Which Fetch? A One Page Reference for Nuxt Data Fetching

Which Fetch? A One Page Reference for Nuxt Data Fetching

Choose among Nuxt’s $fetch, useFetch, useAsyncData, and lazy variants based on rendering, interaction, and data needs, while avoiding common pitfalls with keys, caching, SSR, and payloads.

Reza Baar

Reza Baar

November 10, 2026

1. Ask these first

Work down the list and stop at the first yes.

Is it a user action or a side effect (submitting, deleting, clicking)?

Use $fetch in the handler. There is no render to wait for.

Does it need to be in the HTML on first paint, for SEO or to avoid a loading flash?

useFetch if the source is a URL. useAsyncData if it is an SDK, a DB, or several calls composed together.

Can the page render before this data arrives?

Use useLazyFetch or useLazyAsyncData, then handle the loading state in the template.

Is it browser only, user specific, and not needed for SEO?

Add server: false to whichever you picked above.

Are you already inside a handler, a server route, or a plugin?

Use $fetch. Never nest one data composable inside another.

2. The four tools

What it isSSRDeduped by keyIn payload
useFetch(url, opts)useAsyncData plus $fetch, auto keyYesYesYes
useAsyncData(key, fn, opts)Wraps any async functionYesYesYes
useLazy* variantsSame, with lazy: trueYes, non blockingYesYes
$fetch(url, opts)Configured ofetch instanceOnly where calledNoNo, refetches on hydration

During SSR, $fetch to your own /api/* hits the handler directly, no HTTP roundtrip.

3. Options

OptionDefaultNotes
keyauto for useFetchCache and dedup identity. Required for useAsyncData
servertruefalse skips SSR
lazyfalsetrue does not block navigation
immediatetruefalse waits for execute()
watchreactive optsArray of sources, or false to opt out
transformnoneReshapes before caching. Shrinks the payload
picknoneKeeps listed top level keys
defaultnullGive it [] or {} to skip null guards
dedupecanceldefer keeps the first call instead
deepfalsedata is a shallow ref. true only if you mutate nested props
getCachedDatapayload reuseReuses on hydration and client nav, not on manual refresh()

4. Returns

const { data, status, error, refresh, execute, clear } = await useFetch('/api/products')
dataResult, or default until resolved
statusidle, pending, success, error. Branch on this, not on data
errorThrown error, resets to null on successful retry
refresh()Refetch, keeps current data visible
execute()For immediate: false
clear()Resets state for that key

await is optional. The server waits before rendering either way.

5. Factories

Export from composables/. Both are compiler macros, so Nuxt injects dedup keys at build time.

// app/composables/useAPI.ts
export const useAPI = createUseFetch({
  baseURL: 'https://api.example.com',
  onRequest ({ options }) {
    const { session } = useUserSession()
    if (session.value?.token) {
      options.headers.set('Authorization', `Bearer ${session.value.token}`)
    }
  },
  async onResponseError ({ response }) {
    if (response.status === 401) await navigateTo('/login')
  },
})
const { data: profile } = await useAPI('/me')

Function form when defaults need composables or runtime config:

export const useApiFetch = createUseFetch(callerOptions => ({
  ...callerOptions,
  baseURL: callerOptions.baseURL ?? useRuntimeConfig().public.baseApiUrl,
}))
  • createUseAsyncData works the same way. Best home for a shared getCachedData policy.
  • You cannot name your own composable useFetch. Reserved and compiler transformed.

6. Refetch and cancel

  • Reactive URL or options refetch on their own. A computed, ref, or getter is enough.
  • signal from an AbortController for manual cancellation.
  • Default dedupe already cancels the in flight request for the same key.
  • clearNuxtData(key) drops the cache so the next mount refetches. Use after a mutation.

7. What usually breaks

  • Bare $fetch in <script setup> fetches twice. Nothing enters the payload.
  • Nested data composables break context and key generation. Use $fetch inside handlers.
  • Auto keys collide when URL and options look identical, usually in a v-for. Pass key.
  • Dynamic route params must be in the key, or /products/1 and /products/2 share stale data.
  • transform does not run on a cached read. What was cached is already transformed.
  • Big responses are serialized into the HTML. Use pick or transform.
  • data returning a string means an accidental import { useFetch } from '@vueuse/core'.
  • Call these in setup, a plugin, or route middleware. After an await in setup, context is lost.

8. Changing in Nuxt 5

Testable now with future.compatibilityVersion: 5.

  • clear() and clearNuxtData() restore your default instead of setting data to undefined.
  • clearNuxtState() resets to the useState init value, matching clearNuxtData.

Ready to prove your Nuxt skills?

Get certified in Nuxt

More Nuxt articles

Keep learning Nuxt. Then get certified when you are ready.