The React use API reads the resolved value of a Promise during render, or a context value from anywhere in a component. It is the client-side way to unwrap a Promise without an Effect, and a more flexible alternative to useContext for reading context. Despite its name, use is not a Hook, so it can be called inside conditions and loops.
The use API is stable since React 19. One caveat: reading context with use is not supported in Server Components. Reading a Promise with use is a client-only pattern, while Server Components unwrap Promises with await.
Reading a Promise with use
Pass a Promise to use and the component suspends until it resolves. The nearest Suspense boundary shows its fallback in the meantime, and the resolved value is returned by use, so Albums reads it directly without state or an Effect.
import { use } from "react";
function Albums({ albumsPromise }) {
const albums = use(albumsPromise);
return (
<ul>
{albums.map((album) => (
<li key={album.id}>{album.title} ({album.year})</li>
))}
</ul>
);
}Albums reads the Promise directly instead of storing it in state. Wrap it in Suspense so React has a fallback to show while the Promise is pending.
import { Suspense } from "react";
export default function App() {
return (
<Suspense fallback={<p>Loading albums...</p>}>
<Albums albumsPromise={fetchAlbums()} />
</Suspense>
);
}The list shows the loading paragraph first, then the resolved albums. There is no loading flag to set or clear: Suspense owns the pending state and use owns the read. If the Promise rejects, React walks up to the nearest error boundary instead of rendering the list.
The same call can read a context instead of a Promise, which makes use one API for two different resources. A Promise resolved before use is called can also be read synchronously if its status and value fields are set, a trick library authors use to avoid an extra fallback flash.
Promises must be cached
The Promise you pass must be the same instance across re-renders. A bare fetch call creates a new Promise on every render, which suspends again and again and never reveals content.
function Albums() {
const albums = use(fetch("/albums"));
return <p>{albums.length} albums</p>;
}This reads a fresh Promise each render, so the Suspense fallback repeats forever. The fix is a cache that returns the same Promise for the same key.
const cache = new Map();
export function fetchData(url) {
if (!cache.has(url)) {
cache.set(url, getData(url));
}
return cache.get(url);
}Now every render receives the same Promise. React reads the resolved value synchronously after the first suspension, and frameworks such as Next.js provide this caching for you.
The reason a fresh Promise breaks is that React retries rendering from scratch after a suspension, which recreates anything created during render. See React Suspense Explained for the boundary side of the same pattern.
Reading context with use
Pass a context to use and it returns the closest provider's value. Unlike useContext, the call can sit inside an if or a loop.
import { createContext, use } from "react";
const ThemeContext = createContext("light");
function Button({ show, children }) {
if (show) {
const theme = use(ThemeContext);
return <button className={`button-${theme}`}>{children}</button>;
}
return null;
}The conditional read works because use is not a Hook, so it does not break the Rules of Hooks. It still searches upward for the nearest provider and never considers the component that calls it. This is the main reason to prefer the React use API over useContext: components with early returns or nested conditionals can still read context safely.
Stream a Promise from the server
A Server Component can start a fetch and pass the Promise as a prop to a Client Component, which reads it with use. The Promise crosses the boundary without waiting for the whole page.
// Server Component
import { Message } from "./Message";
export default function App() {
const messagePromise = fetchMessage();
return (
<Suspense fallback={<p>Waiting for message...</p>}>
<Message messagePromise={messagePromise} />
</Suspense>
);
}The Promise is created on the server and resolved on the client. The Server Component keeps fetching while the client reads the value, so only the Message subtree waits.
// Client Component
"use client";
import { use } from "react";
export function Message({ messagePromise }) {
const content = use(messagePromise);
return <p>Here is the message: {content}</p>;
}The Server Component suspends only the Message subtree, so the rest of the page renders immediately. For the full model, see React Server Components Explained.
Errors and fallbacks
Do not wrap use in a try-catch. The API suspends and throws internally, so a try-catch misreads that mechanism.
Let a rejected Promise reach an error boundary instead, and keep a Suspense boundary for the pending state. An error boundary above the Suspense boundary catches the rejection, so loading and failure stay separate concerns.
If you currently load data in an Effect, the React use API removes the manual loading and error bookkeeping. See You Might Not Need an Effect for why Effect-based fetching is usually the long way around.
Rune AI
Key Insights
- use reads the value of a Promise or a context.
- It is not a Hook, so it can run in conditions and loops.
- Pass a cached Promise, never a fresh fetch call.
- Wrap the reader in Suspense and an error boundary.
- Reading context with use is not supported in Server Components.
Frequently Asked Questions
Is use a Hook?
Why does my component suspend forever?
Conclusion
The use API reads a Promise or a context during render. Cache Promises so they survive re-renders, wrap readers in Suspense, and let rejected Promises reach an error boundary.
More in this topic
How to Build a Dropdown Menu in React
Build a React dropdown menu with the ARIA menu button pattern. Handle open and close, keyboard arrows, and clicks outside the menu.
How to Animate React Components with Motion
Animate React components with the Motion library. Set up motion, add enter, hover, and exit animations, and respect reduced motion.
Headless UI Components Explained: Logic Without Locked Styling
Understand headless UI components and how libraries like Radix give you unstyled, accessible behavior that you style yourself.