Efficient data fetching in web applications is important because it directly impacts performance, user experience, and resource usage.
In this article, we'll explore how to use useAsyncData to optimize data fetching and use built-in caching to improve overall performance of our Nuxt application.
What is useAsyncData?
useAsyncData is a composable in Nuxt 3+ that runs your asynchronous data fetch during server-side rendering (SSR) and hydrating on the client.
Unlike client-only fetching, useAsyncData works both on the server and client side, making it ideal for pre-rendering content and improving SEO.
A basic example of using useAsyncData looks like this:
const { data, pending, error } = await useAsyncData("myData", () =>
$fetch("/api/data")
);
In the above example, myData is a unique key to ensure that data fetching can be properly de-duplicated across requests. If you do not provide a key, then a key that is unique to the file name and line number of the instance of useAsyncData will be generated for you.
The () => $fetch('/api/data') callback is responsible for fetching the data. It's an asynchronous function that must return a truthy value (for example, it should not be undefined or null) or the request may be duplicated on the client side.
Keep in mind that by default, useAsyncData will block client-side navigation until all the requests are finished in the useAsyncData.
This can be customized with the lazy option discussed further in the article.
useAsyncData supports many different features that we'll explain later in this article, for example lazy loading, watching keys, caching, and controlling server-side fetching.
Benefits of using useAsyncData
The useAsyncData composable comes with several benefits that make it easy to build a fast website.
Server-Side Data Fetching
When used in a page or component, useAsyncData automatically runs during server-side rendering (SSR) and hydrates the data to the client.
This means faster initial page loads, better SEO because the HTML is pre-filled with content, and reduced client-side data fetching overhead.
Built-in Caching
Nuxt automatically caches data on the server and reuses it when possible. You can even control how often data should be refreshed or cached using options like staleTime or server.
Improved Performance
By shifting data fetching to the server during SSR or SSG (static-site generation), useAsyncData avoids redundant client-side requests and delivers faster perceived performance to users.
Improved Developer Experience by code simplification
Fetching and managing loading and error states often requires boilerplate code. useAsyncData simplifies this with its built-in state handling, making your components cleaner and more maintainable.
New Optimization: shallowRef for data
From Nuxt 4 (and optionally in compatible Nuxt 3), data is now a shallowRef rather than a deep reactive ref. This avoids high overhead on nested data structures, boosting performance dramatically.
To see the actual difference in performance between the usage of ref and shallowRef let’s take a look at the following benchmark:
| Data Size | Deep Reactivity | Shallow Reactivity | Improvement |
|---|---|---|---|
| 100 | 45 ms | 12 ms | 73% |
| 10000 | 850 ms | 95 ms | 59% |
You can see an even more detailed benchmark by visiting this page.
From now on, shallowRef should be used for the majority of cases but if you need to you can always disable it and use the normal ref like so:
const { data } = await useAsyncData("foo", fetchFn, { deep: true });
