The use hook
By Flavio Copes
Learn how the React use() hook reads a promise inside Suspense so React suspends the component until it resolves, even when you call it conditionally.
The use() hook lets a component read the value of a promise. You pass it a promise (or other values, like a context), and React suspends that component until the promise resolves.
Since the component suspends, you wrap it in a Suspense boundary, and React shows the fallback while the data loads:
<Suspense fallback={<Spinner />}>
<Profile userId={123} />
</Suspense>
import { use } from 'react'
async function fetchUser() {
//....
}
export function Profile({ userId }) {
const user = use(fetchUser(userId));
return <h1>{user.name}</h1>;
}
While fetchUser() is pending, React renders the <Spinner /> fallback. When the promise resolves, use() returns the resolved value and the Profile component renders with the data.
Notice what’s missing here: no useState for the data, no useEffect to trigger the fetch, no loading flag to manage by hand. That’s the pattern use() replaces.
Why is it special?
This is a “special” hook because hooks normally must be called at the top of a component, but use() does not have this limitation.
You can call it inside an if, or inside a loop:
export function Profile({ userId }) {
if (!userId) {
return <p>No user selected</p>
}
const user = use(fetchUser(userId));
return <h1>{user.name}</h1>;
}
Try that with useState and React complains about the rules of hooks. With use() it’s allowed. The one rule that remains: you can only call it inside a component or a custom hook.
use() also reads contexts. use(ThemeContext) works like useContext(ThemeContext), with the bonus that you can call it conditionally.
What about errors?
If the promise rejects, React looks for the nearest error boundary and renders its fallback. So the full pattern is a Suspense boundary for the loading state and an error boundary for the failure state. The component itself stays clean, it only handles the happy path.
Be careful where you create the promise
One pitfall: in a client component, don’t create a brand new promise on every render and pass it straight to use(). Each render kicks off a new fetch, and React warns you about suspending on an uncached promise.
The fix is to make the promise stable. Create it outside the component, cache it (your data fetching library probably does this for you), or create it in a server component and pass it down as a prop.
This new function simplifies creating smooth data loading experiences.