Introduction
React 19 introduces use, a new API that breaks one of the oldest rules of hooks: it can be called conditionally. Unlike useContext or useState, use can appear inside if statements, loops, and early returns. It serves two primary purposes: reading the value of a Promise (integrating with Suspense) and reading Context values conditionally. This makes it one of the most flexible tools in the React 19 toolkit.
Key Concepts
use(promise): Reads the resolved value of a Promise. The component suspends (shows a Suspense fallback) until the Promise resolves.use(context): Reads a Context value, just likeuseContext, but with the ability to be called conditionally.- Suspense Integration: When
useencounters an unresolved Promise, it triggers the nearest<Suspense>boundary's fallback. - Conditional Calling: Unlike traditional hooks,
usecan be placed insideifblocks,forloops, and after early returns.
Real World Context
Imagine a dashboard component that only shows a user's premium content if they are logged in. With useContext, you could not place the context read after an early return. With use, you can check a condition first and only read the context when needed. For Promise reading, consider a product page where the product data is fetched as a Promise — use lets the component suspend until the data is ready, and Suspense shows a skeleton loader in the meantime.
Deep Dive
Reading a Promise with use:
tsximport { use, Suspense } from "react"; function UserProfile({ userPromise }: { userPromise: Promise<User> }) { const user = use(userPromise); return ( <div> <img src={user.avatar} alt={user.name} /> <h2>{user.name}</h2> <p>{user.bio}</p> </div> ); } function App({ userId }: { userId: string }) { // Important: create Promise outside render or cache it const userPromise = fetchUser(userId); return ( <Suspense fallback={<ProfileSkeleton />}> <UserProfile userPromise={userPromise} /> </Suspense> ); }
Reading Context conditionally:
tsximport { use } from "react"; import { ThemeContext } from "./contexts"; function OptionalTheme({ useTheme, children }: { useTheme: boolean; children: React.ReactNode }) { if (!useTheme) { return <div className="default">{children}</div>; } // This is valid! use() can be called after an early return const theme = use(ThemeContext); return ( <div style={{ background: theme.bg, color: theme.text }}> {children} </div> ); }
An important detail: when using use with Promises, the Promise should not be created during render. If you write use(fetch(...)) directly inside a component, a new Promise is created on every render, causing an infinite Suspense loop. Instead, create the Promise in a parent component, a loader, or use a caching layer.
Common Pitfalls
- Creating Promises during render — Writing
use(fetchData())inside the component body creates a new Promise every render, causing infinite suspension. Always lift Promise creation outside the component or use a cache. - Forgetting the Suspense boundary — When using
usewith a Promise, there must be a<Suspense>ancestor. Without one, React will throw an error because it has nowhere to show a fallback.
Best Practices
- Cache or hoist Promises — Create Promises in route loaders, parent components, or use a caching mechanism so the same Promise reference is reused across renders.
- Use
use(Context)for conditional reads — If you have components that only need context in certain branches, preferuse(Context)overuseContext(Context)to avoid unnecessary context subscriptions.
Summary
usecan read Promises (triggering Suspense) and Context values, and unlike hooks it can be called conditionally.- Promises passed to
usemust be cached or created outside render to avoid infinite suspension loops. use(Context)is a drop-in replacement foruseContext(Context)with the added benefit of conditional calling.
Code Examples
import { use, Suspense } from "react";
import { ThemeContext } from "./contexts";
// Reading a Promise — suspends until resolved
function UserProfile({ userPromise }: { userPromise: Promise<User> }) {
const user = use(userPromise);
return <h2>{user.name}</h2>;
}
// Reading Context conditionally — not possible with useContext
function ConditionalTheme({ enabled }: { enabled: boolean }) {
if (!enabled) return <div>No theme</div>;
const theme = use(ThemeContext); // Valid after early return!
return <div style={{ color: theme.primary }}>Themed!</div>;
}
// Usage
function App() {
const userPromise = fetchUser("123");
return (
<Suspense fallback={<p>Loading...</p>}>
<UserProfile userPromise={userPromise} />
</Suspense>
);
}