//
Search across all documentation pages
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
import { use, Suspense } from "react";
// Reading a promise -- must be wrapped in Suspense
function UserProfile({ userPromise }: { userPromise: Promise<{ name: string; email: string }> }) {
const user = use(userPromise);
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
}
// Reading context -- replaces useContext
const ThemeContext = React.createContext<"light" | "dark">("light");
function ThemedButton() {
const theme = use(ThemeContext);
return <button className={theme === "dark" ? "bg-gray-800 text-white" : "bg-white"}>Click me</button>;
}
// Usage
function App() {
const userPromise = fetchUser(1); // create promise outside render, or in a Server Component
return (
<ThemeContext value="dark">
<Suspense fallback={<p>Loading user...</p>}>
<UserProfile userPromise={userPromise} />
</Suspense>
<ThemedButton />
</ThemeContext>
);
}When to reach for this: Use use() to read a promise passed from a Server Component or parent, or to conditionally read context in branches where useContext cannot be called.
// A dashboard that conditionally loads different data based on user role
"use client";
import { use, Suspense, createContext } from "react";
type User = { id: string; name: string; role: "admin" | "viewer" };
type Stats = { revenue: number; users: number; orders: number };
type Activity = { id: string; description: string; timestamp: string };
const AuthContext = createContext<User | null>(null);
function DashboardContent({
statsPromise,
activityPromise,
}: {
statsPromise: Promise<Stats>;
activityPromise: Promise<Activity[]>;
}) {
const user = use(AuthContext);
if (!user) {
return <p>Please sign in.</p>;
}
// use() can be called conditionally -- this is unique among React hooks
if (user.role === "admin") {
const stats = use(statsPromise);
return (
<div>
<h2>Admin Dashboard</h2>
<dl>
<dt>Revenue</dt><dd>${stats.revenue.toLocaleString()}</dd>
<dt>Users</dt><dd>{stats.users.toLocaleString()}</dd>
<dt>Orders</dt><dd>{stats.orders.toLocaleString()}</dd>
</dl>
</div>
);
}
const activity = use(activityPromise);
return (
<div>
<h2>Activity Feed</h2>
<ul>
{activity.map((a) => (
<li key={a.id}>
{a.description} <time>{a.timestamp}</time>
</li>
))}
</ul>
</div>
);
}
export default function Dashboard({
user,
statsPromise,
activityPromise,
}: {
user: User | null;
statsPromise: Promise<Stats>;
activityPromise: Promise<Activity[]>;
}) {
return (
<AuthContext value={user}>
<Suspense fallback={<p>Loading dashboard...</p>}>
<DashboardContent
statsPromise={statsPromise}
activityPromise={activityPromise}
/>
</Suspense>
</AuthContext>
);
}What this demonstrates:
use(AuthContext) reading context without useContextuse(promise) called conditionally inside an if block -- impossible with traditional hooksuse() is not a hook in the traditional sense. It does not follow the "rules of hooks" -- you can call it inside loops, conditionals, and early returns. This is what makes it distinct from useContext.use() suspends the component until the promise resolves. The nearest <Suspense> boundary shows the fallback. If the promise rejects, the nearest error boundary catches the error.use() behaves identically to useContext() -- it reads the current value from the nearest provider above in the tree.use() should be stable -- do not create a new promise on every render. Promises should come from Server Components, be cached, or be created outside the render function.use() integrates with React's concurrent features. While a promise is pending, React can continue rendering other parts of the tree.Server Component passing a promise to a Client Component:
// app/page.tsx (Server Component)
import UserCard from "./UserCard";
export default function Page() {
// Start fetching but do NOT await -- let the client resolve it
const userPromise = fetchUser(1);
return <UserCard userPromise={userPromise} />;
}// app/UserCard.tsx
"use client";
import { use } from "react";
export default function UserCard({ userPromise }: { userPromise: Promise<{ name: string }> }) {
const user = use(userPromise);
return <h2>{user.name}</h2>;
}Conditional context reading:
import { use, createContext } from "react";
const FeatureFlagContext = createContext<Set<string>>(new Set());
function FeatureGate({ flag, children }: { flag: string; children: React.ReactNode }) {
const flags = use(FeatureFlagContext);
if (!flags.has(flag)) return null;
return <>{children}</>;
}Chaining promises:
function OrderDetails({ orderPromise }: { orderPromise: Promise<{ id: string; userId: string }> }) {
const order = use(orderPromise);
// You can use the resolved value to create another promise
// but cache it to avoid re-creating on every render
const userPromise = useMemo(() => fetchUser(order.userId), [order.userId]);
const user = use(userPromise);
return (
<div>
<p>Order #{order.id}</p>
<p>Customer: {user.name}</p>
</div>
);
}use<T>(promise: Promise<T>): T -- The resolved type is inferred from the promise.use<T>(context: React.Context<T>): T -- The context value type is inferred.userPromise: Promise<User>.use() is imported from "react": import { use } from "react".fetch() or new Promise() inside the component body creates a new promise on every render, causing infinite suspense loops. Fix: Create the promise in a Server Component, parent component, or use useMemo/cache to stabilize it.<Suspense> ancestor exists when use() reads a promise, React throws an error. Fix: Wrap the component (or an ancestor) in <Suspense fallback={...}>.use() with promises.Promise and Context are supported. Observables, generators, and other async iterables are not. Fix: Convert other async sources to promises before passing them to use().use(MyContext), note that <MyContext.Provider> still works but the preferred syntax is <MyContext value={v}>. Fix: Migrate to the shorter syntax.use() does not cache, deduplicate, or revalidate. It reads a single promise. Fix: Pair with a caching layer (React cache(), framework-level caching, or a library like SWR/TanStack Query).| Approach | When to choose |
|---|---|
use(promise) | Reading a promise from a Server Component or cached source |
useContext(ctx) | Traditional context reading (still works, but cannot be called conditionally) |
use(context) | Conditional context reading in branches |
| TanStack Query / SWR | Full data-fetching with caching, revalidation, pagination |
useEffect + useState | Legacy fetch-on-mount pattern (still works but less ergonomic) |
React.lazy | Code-splitting components (different use case than data promises) |
use() can be called inside loops, conditionals, and early returns -- it does not follow the "rules of hooks"useContext only works with Contextuse() is the preferred replacement for useContext() going forward<Suspense> ancestor exists when use() reads a pending promise<Suspense fallback={...}>Yes. This is the key differentiator from other hooks:
function Dashboard({ user, statsPromise, activityPromise }) {
if (user.role === "admin") {
const stats = use(statsPromise);
return <AdminView stats={stats} />;
}
const activity = use(activityPromise);
return <ActivityFeed activity={activity} />;
}fetch() or new Promise() inside the component body creates a new promise on every renderuseMemo/cache// Server Component -- do NOT await
export default function Page() {
const userPromise = fetchUser(1);
return <UserCard userPromise={userPromise} />;
}
// Client Component
"use client";
import { use } from "react";
export default function UserCard({ userPromise }) {
const user = use(userPromise);
return <h2>{user.name}</h2>;
}use() does not cache, deduplicate, or revalidate datacache(), framework caching, or a library) for production data-fetching needsfunction OrderDetails({ orderPromise }) {
const order = use(orderPromise);
const userPromise = useMemo(
() => fetchUser(order.userId),
[order.userId]
);
const user = use(userPromise);
return <p>Order #{order.id} by {user.name}</p>;
}Use useMemo to stabilize the dependent promise and avoid re-creation on every render.
<MyContext.Provider value={v}> is deprecated<MyContext value={v}> directly as the provider JSXuse(MyContext) and useContext(MyContext) read from either syntaxPromise and React.Context are supporteduse()// Promise -- resolved type inferred
const user: User = use<User>(userPromise);
// Context -- value type inferred
const theme: "light" | "dark" = use(ThemeContext);Import from "react": import { use } from "react".
type Props = {
dataPromise: Promise<{ id: string; name: string }>;
};
function MyComponent({ dataPromise }: Props) {
const data = use(dataPromise);
return <p>{data.name}</p>;
}Reviewed by Chris St. John·Last updated Jul 16, 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥