---
name: data-fetching-best-practices
description: Patterns and best practices for efficient data fetching in Nuxt
---
# Data Fetching Best Practices
Effective data fetching patterns for SSR-friendly, performant Nuxt applications.
## Choose the Right Tool
| Scenario | Use |
| ---------------------------------------- | ----------------------------------- |
| Component initial data | `useFetch` or `useAsyncData` |
| User interactions (clicks, forms) | `$fetch` |
| Third-party SDK/API | `useAsyncData` with custom function |
| Multiple parallel requests | `useAsyncData` with `Promise.all` |
| Reusable API client with shared defaults | `createUseFetch` factory |
## Await vs Non-Await Usage
The `await` keyword controls whether data fetching **blocks navigation**:
### With `await` - Blocking Navigation
```vue
```
- **Server**: Fetches data and includes it in the payload
- **Client hydration**: Uses payload data, no re-fetch
- **Client navigation**: Blocks until data is ready
### Without `await` - Non-Blocking (Lazy)
```vue
Loading...
{{ data }}
```
Equivalent to using `useLazyFetch`:
```vue
```
### When to Use Each
| Pattern | Use Case |
| -------------------------- | ----------------------------------------------- |
| `await useFetch()` | Critical data needed for SEO/initial render |
| `useFetch({ lazy: true })` | Non-critical data, better perceived performance |
| `await useLazyFetch()` | Same as lazy, await only ensures initialization |
## Avoid Double Fetching
### ❌ Wrong: Using $fetch Alone in Setup
```vue
```
### ✅ Correct: Use useFetch
```vue
```
## Use Explicit Cache Keys
### ❌ Avoid: Auto-generated Keys
```vue
```
### ✅ Better: Explicit Keys
```vue
```
## Handle Loading States Properly
```vue
```
## Use Lazy Fetching for Non-critical Data
```vue
```
## Shared Data Across Components
```vue
```
## Centralize API Config with createUseFetch
Instead of hand-rolling a wrapper around `useFetch` (and worrying about whether to `await` it), use the `createUseFetch` factory. It produces a fully typed composable with shared `baseURL`, headers, and interceptors:
```ts
// app/composables/useAPI.ts
export const useAPI = createUseFetch({
baseURL: 'https://api.nuxt.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')
},
})
```
```vue
```
For lower-level control, create a custom `$fetch` instance in a plugin and wrap it with `useAsyncData` (or pass it to `createUseFetch`) — this avoids double fetching during SSR:
```ts
// app/plugins/api.ts
export default defineNuxtPlugin(() => {
const api = $fetch.create({ baseURL: 'https://api.nuxt.com' })
return { provide: { api } }
})
```
```vue
```
## Avoid useAsyncData for Side Effects
### ❌ Wrong: Side Effects in useAsyncData
```vue
```
### ✅ Correct: Use callOnce for Side Effects
```vue
```