Nuxt gives you three ways to load data: useFetch, useAsyncData and $fetch. They look similar, but picking the wrong one causes double requests or hydration warnings. Here is when to use each.
useFetch: page and component data#
Use it in <script setup> for data the page needs to render. It runs on the server, sends the result to the browser in the page payload, and does not fetch again during hydration.
const { data: projects, pending, error } = await useFetch('/api/projects', {
query: { page: 1 }
})
useAsyncData: custom logic#
Use it when the data does not come from a single URL, for example when you combine two calls or use an SDK:
const { data } = await useAsyncData('home', async () => {
const [projects, articles] = await Promise.all([
$fetch('/api/projects'),
$fetch('/api/articles')
])
return { projects, articles }
})
Give every call a unique key. Two calls with the same key share one result.
$fetch: user actions#
Use plain $fetch inside event handlers like form submits, "Load more" buttons and search. These run only in the browser, after a click, so there is nothing to hydrate.
async function sendMessage() {
await $fetch('/api/contact', { method: 'POST', body: form })
}
Avoid hydration mismatches#
A mismatch happens when the server and the browser render different HTML. Common causes:
- Calling
$fetchat the top of<script setup>, which fetches twice. - A key that changes between server and browser, such as a different base URL in the key.
- Rendering dates or random values without a stable seed.
Quick rule#
- Needed to render the page:
useFetch. - Several sources or custom logic:
useAsyncData. - After a click or submit:
$fetch.




