When I first heard about React.use, I was excited for React to get a built-in way to fetch data in Client Components. I wanted to go all-in on Suspense back in 2019, and gave a talk about it at React Day Berlin, but the APIs ended up changing significantly with the introduction of Server Components.
Here’s my experience trying use out.
Infinite loop
Knowing it works with Suspense, my first attempt (without reading the docs closely) was to write a component like this:
function Todos() {
const todos = use(fetch('/api/todos').then(resp => resp.json()));
return <ul>{todos.map(todo => <li key={todo.id}>{todo.title}</li>)}</ul>
}
function TodoListApp() {
return <Suspense fallback='loading todos...'><Todos /></Suspense>
}
Fetch-on-render aside, this is a classic thing to do in a React component that used to involve coordinating useEffect and useState to accomplish. I was so glad to have a built-in primitive instead!
To my surprise, a new promise was being created on every single render (including every time the promise resolved), resulting in an infinite loop.
This seemed like such a simple use case that I was shocked I had already run into trouble.
No promise caching primitive
Turns out that the third bullet point in a list of caveats in the docs has a warning about this. I wish it was mentioned earlier.
The RFC for use says:
This will be covered extensively in the upcoming RFC for the
cacheAPI. It’s very unlikely we will shipusewithoutcache.
An unrelated cache API did ship, but it’s more of a server version of useMemo. It’s only for use in Server Components, and the use docs explicitly recommend against using the use API in Server Components.
So, we’re left without a primitive for caching async calls in Client Components in React.
Confusing naming
Calling something use means it is really hard to search for. Here’s the result of my trying to find use examples in the Next.js docs:

It only shows basic “How to use” guides. I did eventually find one spot where use is mentioned, but that was by chance when I was trying to learn more about the cache api.
use means it’s a hook, right?
For a while, I was calling the use API the “use hook”. One of the fundamental rules of modern React is that hook names always start with use. However, it turns out that use isn’t a hook at all.
On the bright side, it means I don’t need to obey the rules of hooks when I call it. I can finally make a fetch conditionally!
The downside is that it comes with its own rules. It can’t be called within a try-catch block (understandable, knowing that Suspense relies on throwing Promises).
This was really confusing to me. The RFC has a small explainer, but I didn’t find it satisfying, and given the number of comments on the RFC that question the naming, I’m not alone.
The word use also shows up in React’s 'use client' and 'use server' source code directives.
Prior art
lazy has a different purpose (and different rules) but that’s a much clearer name.
Libraries like Tanstack Query’s useQuery and React Router’s useAsyncValue solve the promise caching caveats that use has, though they do need to obey the rules of hooks.
My favorite alternative is actually React Router’s Await component. Since use can trigger Suspense, and we see Suspense in the component tree, having Awaits in the component tree seems much more intuitive, and makes it clear where I might want to add more Suspense boundries. Instead of being worried that any hook or component could trigger Suspense anywhere in the app, instead it’s clearer that something async is going to happen.
Feature creep
use is also a replacement for useContext. The useContext docs don’t mention this anywhere, but use is a way to get context values without having to obey the rules of hooks. Per the use docs:
useis preferred overuseContextbecause it is more flexible.
This is genuinely useful, but why not create another API called getContext, even if it is the same implementation under the hood?
Maybe I’m just using it wrong
The use docs are clear that the primary use case for use is to kick off a promise in a Server Component, and pass its value to a Client Component. For that, the API is straightforward.
It is different, though. The promise executes on the server and the result is sent to the client when it is done, meaning you can’t use client APIs in the promise, and the result of the Promise must be serializable. Meta-frameworks don’t seem to be aligning with this though, instead implementing their own solutions like Next.js’ upcoming Streaming functionality or React Router’s Await.
Maybe we will get a built-in client-side cache API to be able to use promises natively in React Client Components some day.