Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/dvlkit/nuxe/llms.txt

Use this file to discover all available pages before exploring further.

useAsyncData is Nuxe’s core data-fetching primitive. On the server it runs your async handler, stores the result in the SSR payload, and ships it to the browser. On the client the payload is read back and the handler is not called again — eliminating duplicate network requests across the hydration boundary. Subsequent client-side navigations re-run the handler normally.
Every useAsyncData call is deduplicated by key. Two calls with the same key in the same request share a single in-flight promise and a single serialized result. Keys are also used as the payload property name during SSR hydration, so they must be unique across the app.

Signature

useAsyncData<T>(
  key: AsyncDataKey,
  handler: () => Promise<T>,
  options?: UseAsyncDataOptions<T>
): UseAsyncDataReturn<T>

AsyncDataKey

The key argument accepts three forms:
type AsyncDataKey = string | Ref<string> | (() => string)
When a Ref<string> or getter function is supplied, useAsyncData watches the resolved value and automatically re-fetches whenever it changes.

Parameters

key
string | Ref<string> | (() => string)
required
A unique identifier for this async data entry. Used for SSR payload serialization, client-side hydration, and deduplication. When key is a Ref or a getter function, the resolved string value is watched — a key change clears data and triggers a fresh fetch.
handler
() => Promise<T>
required
An async function that returns the data. Executed on the server during SSR (unless server: false) and on the client for any fetch that cannot be hydrated from the SSR payload.
options
UseAsyncDataOptions<T>
Optional configuration object. All fields are optional.
options.default
() => T | Ref<T>
A factory function that returns the initial value of data before the handler resolves. Useful to avoid null checks in templates while the first fetch is in-flight.
options.server
boolean
default:"true"
Set to false to skip running the handler during SSR. The fetch will happen on the client after hydration instead.
options.lazy
boolean
default:"false"
When true, the handler runs after the component mounts rather than blocking navigation. pending starts as true and data is populated asynchronously. This avoids delaying page render at the cost of a loading state.
options.retryCount
number
default:"0"
Number of additional attempts to make after the first failure before the error is surfaced. Set to 3 to retry up to three extra times.
options.retryDelayMs
number
default:"0"
Milliseconds to wait between retry attempts. Only applied when retryCount is greater than 0.
options.watch
WatchSource | WatchSource[]
One or more Vue watch sources. Whenever any source changes, useAsyncData re-runs the handler and updates data. This is the recommended way to re-fetch when a filter, page number, or other reactive value changes.

Return Values

data
Ref<T | null>
The resolved value returned by handler. Starts as null (or the value returned by options.default() if provided) and is updated once the handler settles.
pending
Ref<boolean>
true while a fetch is in progress, false otherwise. When lazy: true, starts as true until the initial fetch completes.
error
Ref<Error | null>
The last error thrown by handler, or null if the last fetch succeeded (or no fetch has run yet).
status
Ref<'idle' | 'pending' | 'success' | 'error'>
A string enum that summarises the composite state:
ValueMeaning
'idle'No fetch has started yet
'pending'A fetch is currently in-flight
'success'The last fetch completed successfully
'error'The last fetch threw an error
refresh
() => Promise<void>
Imperatively re-run the handler. Sets status to 'pending' while running and resolves once the new result (or error) is stored. Useful for pull-to-refresh or polling patterns.

Framework Utilities

setHydratedPayload

Populates the client-side hydration payload from the SSR-rendered page data. This is called automatically by the Nuxe runtime after the HTML is parsed — you do not need to call it manually in application code.
setHydratedPayload(payload: Record<string, unknown>): void

Example

<script setup lang="ts">
import { useAsyncData } from '@dvlkit/nuxe'

interface Post {
  id: number
  title: string
  body: string
}

const { data: post, pending, error, refresh } = await useAsyncData<Post>(
  'post-1',
  () => $fetch('/api/posts/1'),
  {
    default: () => ({ id: 0, title: 'Loading…', body: '' }),
    retryCount: 2,
    retryDelayMs: 500,
  },
)
</script>

<template>
  <div>
    <p v-if="pending">Fetching post…</p>
    <p v-else-if="error" class="error">{{ error.message }}</p>
    <article v-else>
      <h1>{{ post?.title }}</h1>
      <p>{{ post?.body }}</p>
    </article>
    <button @click="refresh">Refresh</button>
  </div>
</template>

Reactive key example

When the key is derived from reactive state, useAsyncData automatically re-fetches when the key changes:
const postId = ref(1)

const { data } = useAsyncData(
  () => `post-${postId.value}`,
  () => $fetch(`/api/posts/${postId.value}`),
)

// Changing postId triggers a new fetch automatically
postId.value = 2

Watching external sources

Use watch to re-fetch when unrelated reactive state changes (e.g., a filter that affects the request body but not the key):
const filter = ref('published')

const { data: posts } = useAsyncData(
  'posts',
  () => $fetch('/api/posts', { query: { filter: filter.value } }),
  { watch: [filter] },
)
Prefer a reactive key (getter form) over watch when the key itself incorporates the changing value. Reserve watch for cases where the request changes but the logical cache entry stays the same.

Build docs developers (and LLMs) love