use Hook
Read the value of a promise or context during render — React 19's new primitive for async data and conditional context.
Recipe
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.
Working Example
"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)suspendsUserProfileuntil the promise resolves- The
Suspenseboundary shows a fallback while the promise is pending - Creating a new promise (via
setUserPromise) triggers a new suspension - The promise is created in the parent and passed as a prop — not created during render
Deep Dive
How It Works
usecan read two types of values: Promises and React Contexts- For promises:
usesuspends the component until the promise resolves, then returns the resolved value. If the promise rejects, it throws to the nearest error boundary - For context:
usereads the nearest provider's value, just likeuseContext, but unlikeuseContext, it can be called inside conditions, loops, and early returns useis 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)- The promise must be created outside the rendering component or cached — creating a new promise on every render causes infinite suspension
Parameters & Return Values
| 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 |
Variations
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>;
}TypeScript Notes
// 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");Gotchas
-
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,
usethrows the error. Without an error boundary, this crashes the app. Fix: Always wrapuse-consuming components in an error boundary. -
Not a full hook —
usecan be called conditionally, but it still can only be called during render. Calling it in an event handler oruseEffectdoes not work. Fix: Calluseonly in the component body during render. -
Promise identity matters — If you pass a new promise object (same URL but new
fetchcall), 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 —
usehandles reading a promise with Suspense, but it does not provide caching, deduplication, or revalidation. Fix: Useusewith a caching layer, or use a data fetching library like TanStack Query.
Alternatives
| 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.
FAQs
What types of values can the use hook read?
usecan read two types: Promises and React Contexts.- For promises, it suspends the component until the promise resolves.
- For context, it works like
useContextbut can be called conditionally.
Why is use not technically a hook even though it starts with "use"?
- Unlike hooks,
usedoes not follow the "rules of hooks" for conditional calls. - You can call
useinsideifstatements, loops, and after early returns. - However, it can only be called during render -- not in event handlers or effects.
Gotcha: Why does use(fetch("/api/data")) cause infinite suspension?
- Calling
fetch()inside the component creates a new promise on every render. - React sees a new promise each time and re-suspends, creating an infinite loop.
- Create the promise outside the component, in a parent, or in a cache.
What happens if the promise passed to use rejects?
usethrows the error, which propagates to the nearest error boundary.- Without an error boundary, the rejection crashes the app.
- Always wrap
use-consuming components in anErrorBoundary.
How do I cache a 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)); // same promise object
return <div>{data.title}</div>;
}How does use(Context) differ from useContext(Context)?
use(Context)can be called inside conditions, loops, and after early returns.useContext(Context)must follow the rules of hooks -- top level only.- Both read the nearest Provider's value. Choose
usewhen you need conditional reads.
Can I use the use hook in event handlers or useEffect?
- No.
usecan only be called during render (the component body). - In event handlers, use
awaitdirectly or manage state withuseState. - In effects, use
async/awaitwithuseEffect.
How do I type the use hook in TypeScript?
// 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);Gotcha: Why does promise identity matter when using use?
- If you pass a new promise object (same URL but new
fetch()call), React treats it as a new resource. - This causes re-suspension even if the data hasn't changed.
- Cache or memoize promises so the same logical request returns the same promise object.
When should I use use vs useEffect + useState for data fetching?
- Prefer
usewhen you want Suspense-based streaming and the promise is created outside the component. - Use
useEffect+useStatewhen you need fine-grained loading/error control or fetch based on client interactions. usedoes not provide caching, deduplication, or revalidation -- pair it with a caching layer for production use.
How do I pass a promise from a server component to a client component?
// 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>;
}Related
- useContext — traditional context reading (cannot be conditional)
- useActionState — form actions that produce promises
- useTransition — wrap state updates that create new promises
- useOptimistic — show predicted state while promises resolve