React 19 introduces use(), a revolutionary API that lets you read resources (Promises and Context) during render. Unlike hooks, use() can be called conditionally.
Reading Promises
tsximport { use, Suspense } from 'react'; async function fetchUser(id: string): Promise<User> { const response = await fetch(`/api/users/${id}`); return response.json(); } function UserProfile({ userPromise }: { userPromise: Promise<User> }) { // use() suspends until the promise resolves const user = use(userPromise); return ( <div> <h1>{user.name}</h1> <p>{user.email}</p> </div> ); } function App() { const userPromise = fetchUser('123'); // Create promise in parent return ( <Suspense fallback={<Loading />}> <UserProfile userPromise={userPromise} /> </Suspense> ); }
Reading Context Conditionally
Unlike useContext, use() can be called inside conditionals:
tsxfunction StatusMessage({ showDetails }: { showDetails: boolean }) { if (showDetails) { // This is allowed with use()! const theme = use(ThemeContext); return <div className={theme}>Detailed status...</div>; } return <div>Basic status</div>; }
Key Differences from Hooks
| Aspect | Hooks (useContext, etc.) | use() |
|---|---|---|
| Conditional calls | ❌ Not allowed | ✅ Allowed |
| Loops | ❌ Not allowed | ✅ Allowed |
| Promises | ❌ Not supported | ✅ Suspends |
| Early returns | ❌ Must be before | ✅ Can be after |
Important Rules
- Create promises outside the component or memoize them
- Wrap in Suspense to handle the loading state
- Handle errors with Error Boundaries
tsx// 🔴 Bad - creates new promise every render function BadExample({ userId }) { const user = use(fetchUser(userId)); // Infinite loop! } // ✅ Good - promise created in parent function GoodExample({ userPromise }) { const user = use(userPromise); } // ✅ Good - promise cached/memoized function AlsoGood({ userId }) { const userPromise = useMemo(() => fetchUser(userId), [userId]); const user = use(userPromise); }