useAsyncData vs useFetch vs onMounted
These three look similar, but they differ in timing and server–client behavior.
Here’s how to choose the right one, with SSR (Server-Side Rendering) vs CSR (Client-Side Rendering) in mind.
What makes Nuxt different?
In plain Vue, you might fetch inonMounted or a custom fetch. Nuxt, with SSR, can fetch data on the server and inline it into the initial HTML. Pages load faster and SEO improves.In Nuxt 3, data fetching isn’t just a detail—it’s part of your render strategy.
useAsyncData
useAsyncData is SSR-friendly and runs on both server and client. It loads data before the page renders so content is ready on first paint.<script setup>
const { data: products, pending, error } = await useAsyncData('products', () => $fetch('/api/products'))
</script>
When to use
- When data should be ready on first load.
- When SEO matters (blogs, e-commerce, news).
- When you want cache across navigations.
Pros
- Full SSR support.
- Automatic key-based caching.
- Built-in
pending,error,refresh. - Readable code.
Cons
- Usable only during setup.
- Each
awaitcan delay render if overused.
'products'), Nuxt returns cached data.useFetch
useFetch is a convenience wrapper around useAsyncData that inlines $fetch.<script setup>
const { data, pending, error } = await useFetch('/api/posts')
</script>
When to use
- Simple GETs.
- Static endpoints like
/api/posts. - You still get SSR and caching.
Pros
- One-liner data fetching.
- Automatic JSON parsing via
$fetch. - SSR-compatible.
Cons
- Less flexible for complex params/transforms.
- Slightly less control than
useAsyncData.
useFetch = useAsyncData + $fetch convenience.onMounted
onMounted is classic Vue and runs client-side only—it doesn’t participate in SSR.<script setup>
import { ref, onMounted } from 'vue'
const posts = ref([])
onMounted(async () => {
const res = await fetch('/api/posts')
posts.value = await res.json()
})
</script>
When to use
- Data is only needed in the browser (user-specific content).
- In non-SSR components.
- After interactions (scroll/click) for dynamic loading.
Pros
- Reduces SSR work.
- Ideal for small dynamic pieces.
Cons
- Not SEO-friendly.
- Page may load empty and fill later (flicker).
SSR vs CSR comparison
| Property | useAsyncData | useFetch | onMounted |
|---|---|---|---|
| Runtime | SSR + CSR | SSR + CSR | CSR only |
| SEO | ✅ | ✅ | ❌ |
| Caching | ✅ | ✅ | ❌ |
| Ease of use | Medium | Easy | Simple |
| Performance | Most optimized | Optimized | Lightest runtime cost |
Real-world scenarios
Blog page:const { data: posts } = await useAsyncData('posts', () => $fetch('/api/posts'))
SSR needed → useAsyncData ✅Dashboard widget:
const { data } = await useFetch('/api/stats')
Small payload, quick render →
useFetch ✅User-specific data:
onMounted(async () => {
const profile = await fetchUser()
})
Requires token/cookies →
onMounted ✅Advanced: lazy & parametric requests
BothuseAsyncData and useFetch support lazy and server options:const { data, refresh } = await useAsyncData('user', () => $fetch(`/api/user/${id}`), {
lazy: true,
server: false
})
lazy: true→ don’t block initial render; fetch later.server: false→ skip SSR; run only on client.
Conclusion
Nuxt 3 gives you three solid tools:- useAsyncData: SSR-first, SEO-friendly, cached.
- useFetch: Simple and practical for straightforward requests.
- onMounted: Client-only for user-specific or interaction-driven loads.
- Need SEO → useAsyncData
- Simple API → useFetch
- User/private data → onMounted