use Hook
Read the value of a promise or context during render - React 19's new primitive for async data and conditional context.
Search across all documentation pages
Read the value of a promise or context during render - React 19's new primitive for async data and conditional context.
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
Quick-reference recipe card - copy-paste ready.
// Read a promise (suspends until resolved)
const data = use(dataPromise);
// Read context (works in conditions and loops - unlike useContext)
if (showTheme) {
const theme = use(ThemeContext);
}When to reach for this: You have a promise created outside the component (passed as a prop or from a cache) and want to read its value with Suspense, or you need to read context conditionally.
"use client";
import { Suspense, use, useState } from "react";
interface User {
id: number;
name: string;
email: string;
}
function fetchUser(id: number): Promise<User> {
return fetch(`https://jsonplaceholder.typicode.com/users/${id}`).then((res) =>
res.json()
);
}
function UserProfile({ userPromise }: { userPromise: Promise<User> }) {
const user = use(userPromise);
return (
<div className="border rounded p-4">
<h3 className="font-semibold">{user.name}</h3>
<p className="text-sm text-gray-600">{user.email}</p>
</div>
);
}
export function UserLoader() {
const [userId, setUserId] = useState(1);
const [userPromise, setUserPromise] = useState(() => fetchUser(1));
function handleLoadUser(id: number) {
setUserId(id);
setUserPromise(fetchUser(id));
}
return (
<div className="space-y-3">
<div className="flex gap-2">
{[1, 2, 3].map((id) => (
<button
key={id}
onClick={() => handleLoadUser(id)}
className={`px-3 py-1 border rounded ${
userId === id ? "bg-blue-600 text-white" : ""
}`}
>
User {id}
</button>
))}
</div>
<Suspense fallback={<p className="text-sm text-gray-500">Loading user...</p>}>
<UserProfile userPromise={userPromise} />
</Suspense>
</div>
);
}What this demonstrates:
use(userPromise) suspends UserProfile until the promise resolvesSuspense boundary shows a fallback while the promise is pendingsetUserPromise) triggers a new suspensionuse can read two types of values: Promises and React Contextsuse suspends the component until the promise resolves, then returns the resolved value. If the promise rejects, it throws to the nearest error boundaryuse reads the nearest provider's value, just like useContext, but unlike useContext, it can be called inside conditions, loops, and early returnsuse is not technically a hook - it does not follow the "rules of hooks" regarding conditional calls. However, it can only be called during render (not in event handlers or effects)| Parameter | Type | Description |
|---|---|---|
resource | Promise<T> or Context<T> | A promise to read, or a React context to consume |
| Return | Type | Description |
|---|---|---|
value | T | The resolved value of the promise, or the current context value |
Conditional context reading:
function Toolbar({ isLoggedIn }: { isLoggedIn: boolean }) {
if (isLoggedIn) {
const user = use(UserContext); // This is allowed - unlike useContext
return <p>Welcome, {user.name}</p>;
}
return <p>Please log in</p>;
}Reading a cached promise (common pattern):
// Cache the promise so it's not recreated on every render
const cache = new Map<string, Promise<Data>>();
function getData(key: string): Promise<Data> {
if (!cache.has(key)) {
cache.set(key, fetch(`/api/${key}`).then((r) => r.json()));
}
return cache.get(key)!;
}
function DataDisplay({ dataKey }: { dataKey: string }) {
const data = use(getData(dataKey));
return <div>{data.title}</div>;
}With error boundary:
<ErrorBoundary fallback={<p>Something went wrong</p>}>
<Suspense fallback={<Spinner />}>
<AsyncComponent dataPromise={promise} />
</Suspense>
</ErrorBoundary>Server component passing promise to client component:
// Server Component
async function Page() {
const dataPromise = fetchData(); // Don't await - pass the promise
return (
<Suspense fallback={<Loading />}>
<ClientComponent dataPromise={dataPromise} />
</Suspense>
);
}
// Client Component
"use client";
function ClientComponent({ dataPromise }: { dataPromise: Promise<Data> }) {
const data = use(dataPromise);
return <div>{data.title}</div>;
}// Type is inferred from the promise or context
const user = use(userPromise); // User (inferred from Promise<User>)
const theme = use(ThemeContext); // Theme (inferred from Context<Theme>)
// Explicit typing when needed
const data = use<ApiResponse>(dataPromise);
// Context type narrowing works the same as useContext
const ctx = use(MyContext);
if (!ctx) throw new Error("Missing provider");Creating promises during render - Writing use(fetch("/api/data")) inside a component creates a new promise on every render, causing infinite suspension. Fix: Create the promise outside the component, in a parent, or in a cache.
No rejection handling in the component - If the promise rejects, use throws the error. Without an error boundary, this crashes the app. Fix: Always wrap use-consuming components in an error boundary.
Not a full hook - use can be called conditionally, but it still can only be called during render. Calling it in an event handler or useEffect does not work. Fix: Call use only in the component body during render.
Promise identity matters - If you pass a new promise object (same URL but new fetch call), React treats it as a new resource and re-suspends. Fix: Cache or memoize your promises so the same logical request returns the same promise object.
Not a replacement for data fetching libraries - use handles reading a promise with Suspense, but it does not provide caching, deduplication, or revalidation. Fix: Use use with a caching layer, or use a data fetching library like TanStack Query.
| Alternative | Use When | Don't Use When |
|---|---|---|
useEffect + useState | You need to fetch data and manage loading/error state manually | You want Suspense-based loading states |
useContext | You need context but don't need conditional reads | You need to read context inside conditions or loops |
| TanStack Query | You need caching, deduplication, revalidation, and pagination | Simple one-time data reads with Suspense |
| Server Components | Data can be fetched entirely on the server | Data depends on client-side state |
await in Server Components | You're in a server component and can directly await | You're in a client component |
When to use use vs useEffect for data? Prefer use when you want Suspense-based streaming and the promise is created outside the rendering component. Use useEffect when you need fine-grained control over loading/error states or need to fetch based on client-side interactions.
use can read two types: Promises and React Contexts.useContext but can be called conditionally.use does not follow the "rules of hooks" for conditional calls.use inside if statements, loops, and after early returns.fetch() inside the component creates a new promise on every render.use throws the error, which propagates to the nearest error boundary.use-consuming components in an ErrorBoundary.const cache = new Map<string, Promise<Data>>();
function getData(key: string): Promise<Data> {
if (!cache.has(key)) {
cache.set(key, fetch(`/api/${key}`).then(r => r.json()));
}
return cache.get(key)!;
}
function DataDisplay({ dataKey }: { dataKey: string }) {
const data = use(getData(dataKey)); // same promise object
return <div>{data.title}</div>;
}use(Context) can be called inside conditions, loops, and after early returns.useContext(Context) must follow the rules of hooks -- top level only.use when you need conditional reads.use can only be called during render (the component body).await directly or manage state with useState.async/await with useEffect.// Type is inferred from the promise or context
const user = use(userPromise); // User (from Promise<User>)
const theme = use(ThemeContext); // Theme (from Context<Theme>)
// Explicit generic when needed
const data = use<ApiResponse>(dataPromise);fetch() call), React treats it as a new resource.use when you want Suspense-based streaming and the promise is created outside the component.useEffect + useState when you need fine-grained loading/error control or fetch based on client interactions.use does not provide caching, deduplication, or revalidation -- pair it with a caching layer for production use.// Server Component
async function Page() {
const dataPromise = fetchData(); // Don't await
return (
<Suspense fallback={<Loading />}>
<ClientComponent dataPromise={dataPromise} />
</Suspense>
);
}
// Client Component
"use client";
function ClientComponent({ dataPromise }: { dataPromise: Promise<Data> }) {
const data = use(dataPromise);
return <div>{data.title}</div>;
}Reviewed by Chris St. John·Last updated Jul 10, 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥