use reads a resource: a promise or a context. It breaks the usual hook rules — it may be called inside an if, inside a loop, or several times in one component — because it does not depend on call order. Given a promise it returns the resolved value; while the promise is pending it suspends the component, so the nearest <Suspense> boundary takes over. A rejected promise reaches the nearest error boundary, which is why use must never be wrapped in try/catch.
The rule that catches everybody: the promise must be the same object on every attempt. React 7,897 discards the state of a component that suspends before mounting and renders it from scratch, so a promise created during render is created anew each time and the component suspends forever.
import { use, Suspense } from 'react';
const cache = new Map();
function getUser(id) { // one promise per id, reused across renders
if (!cache.has(id)) {
cache.set(id, fetch(`https://api.example.com/users/${id}`).then((r) => r.json()));
}
return cache.get(id);
}
function Profile({ id }) {
const user = use(getUser(id)); // suspends only on the first attempt
return <h2>{user.name}</h2>;
}
export default function App({ id }) {
return <Suspense fallback={<p>Loading profile...</p>}><Profile id={id} /></Suspense>;
}Reading context with use(ThemeContext) behaves like useContext but can be called conditionally. React 19.3 added a third resource: use(browser()), imported from react-dom, suspends during server rendering only, so a widget that needs window shows its fallback on the server and the real thing in a browser.