cf.completefrontend
Open lab My progress
THE REACT FIELD GUIDE

The use API

Read a promise or a context while rendering with React's use API: Suspense, error boundaries, promises that last, and calling use inside an if.

By the end, you can
  • 01
    Read a promise with useCall use(promise) inside a Suspense boundary, and say what it does while the promise is pending, fulfilled, or rejected.
  • 02
    Give use a promise that lastsGet the promise from a cache, a parent's state, or a Server Component, and never make it in the component's own render.
  • 03
    Read context with useCall use(context) after an early return or inside an if, and know the places use can't go, like try.

Read a promise's value while rendering

In Suspense, a component read its data with a short line, use(fetchData(…)), and a boundary showed a fallback while it waited. This lesson is about that line. use is a function from React, added in React 19, that reads a value from a resource: a promise or a context. Here it reads an order from a pretend server. The result is live:

An order status read with usePop out in the code editor (opens in a new tab)JSX
import { Suspense, use } from "react";import { fetchData } from "./data.js"; function OrderStatus() {  const order = use(fetchData("/orders/42"));  return <p>Order {order.id}: {order.status}</p>;} export default function App() {  return (    <>      <h3>Your orders</h3>      <Suspense fallback={<p>Checking order 42…</p>}>        <OrderStatus />      </Suspense>    </>  );}
ResultLoading…

Line 5 calls fetchData("/orders/42") from the data.js tab. It starts a request and returns a promise: an object that stands for a value that isn't there yet. use takes that promise and gives back its value, the order. Line 6 shows two of the order's fields. Lines 13 to 15 wrap OrderStatus in a Suspense boundary, which shows Checking order 42… until the order arrives, one second later. Press Run again under the result to watch it once more.

Notice what isn't there: no useState for the order, no useEffect to start the request, no if (order === null) check. OrderStatus reads the order as if it were already there, and React takes care of the wait. The data.js tab is the same in every example of this lesson: it answers after a delay with setTimeout, and nothing goes over the network.

A function card, use(promise), with three arrows. Pending: use suspends, and the page shows Checking order 42…. Fulfilled: use returns the value, and the page shows Order 42: Out for delivery. Rejected: use throws the error, and the page shows the error boundary's fallback.use(promise)pending: use suspendsChecking order 42…fulfilled: use returns the valueOrder 42: Out for deliveryrejected: use throws the errorThe error boundary's fallback
One call, three outcomes. While the promise is pending, use suspends and the nearest Suspense boundary shows its fallback. Once it's fulfilled, use returns the value. If it's rejected, use throws the error, and the nearest error boundary shows its fallback.
Real-life analogyA pickup counter

At a café's pickup counter, you show your ticket. If the order isn't ready, you step aside and the screen says "Preparing"; you come back when your number is called. If it's ready, you take it. If something went wrong, the manager deals with it, not the person at the till.

In real life: You hand in your order ticket at the counter
In React: OrderStatus calls use(promise)
In real life: Not ready: you step aside, and a sign says “Preparing”
In React: Pending: use suspends, and Suspense shows the fallback
In real life: Ready: you get your order straight away
In React: Fulfilled: use returns the value
In real life: Sold out: a manager comes over and explains
In React: Rejected: use throws, and an error boundary shows its fallback
The main idea

use(resource) reads a promise or a context while a component renders. With a promise, it returns the value once the promise is fulfilled, suspends the component while it's pending (the nearest Suspense boundary shows its fallback), and throws the error if it's rejected (the nearest error boundary shows its fallback). Unlike hooks, it can be called inside if and loops.

What use does while the promise waits

use can't stop JavaScript and wait: a component is a plain function, and it has to return something now. So when the promise is still pending, use suspends the component. It stops the function right there, by throwing a special signal that only React catches, and React shows the nearest Suspense fallback instead. When the promise settles, React calls the component again, from its first line. Two log lines make it visible:

Log before and after usePop out in the code editor (opens in a new tab)JSX
import { Suspense, use } from "react";import { fetchData } from "./data.js"; function OrderStatus() {  console.log("OrderStatus starts");  const order = use(fetchData("/orders/42"));  console.log(`use gave back: ${order.status}`);  return <p>Order {order.id}: {order.status}</p>;} export default function App() {  return (    <Suspense fallback={<p>Checking order 42…</p>}>      <OrderStatus />    </Suspense>  );}
ResultLoading…

Open the console under the result. At first it shows OrderStatus starts but no use gave back line: line 5 ran, line 6 suspended, and lines 7 and 8 never ran. A second later, both lines appear: React called OrderStatus again, and this time line 6 returned the order straight away. You see each line twice because the preview runs in StrictMode, which renders every component twice in development to catch mistakes.

How does the second call get the value at once? React marks the promise. The first time use sees a promise, React writes status: "pending" on it and adds a callback. When the promise settles, the callback writes status: "fulfilled" and the value (or "rejected" and the reason). On the next call, use reads those fields instead of waiting. Try all three states here:

Playground: one promise, three outcomes
The pagepending
The promise, as React marked it
status
not set
value
not set
reason
not set
Try it yourself
The promise is pending

use found a promise with no status, so React wrote status: "pending" on it and suspended OrderStatus. The Suspense boundary shows its fallback. Nothing after the use line ran.

This page runs here, with React's real Suspense and use: an OrderStatus that reads a promise you settle with the buttons, inside a Suspense boundary and the ErrorBoundary from the examples. The right side shows the fields React wrote on the promise. Your own code never reads them; they're how use knows the value without waiting a second time. Reset makes a new pending promise.

Read the table row by row: the state of the promise, what use does, and what the reader sees.

What use does for each state of the promise
The promise isWhat use doesWhat the reader sees
Pending: no answer yetIt suspends the component. The lines after it don't run.The nearest Suspense boundary's fallback, like Checking order 42….
Fulfilled: the answer cameIt returns the value at once.The component, built from the value.
Rejected: the request failedIt throws the reason, the error the promise rejected with.The nearest error boundary's fallback.
Only the part that waits

The fallback replaces everything inside the nearest Suspense boundary, and nothing outside it. In the first example, the heading Your orders sat outside the boundary, so it showed at once. With no boundary at all, React has nothing to show in its place, and the whole page waits. Exercise 2 lets you see that.

The promise must be the same on every render

The Suspense lesson showed a page that waited forever because its component made its promise while rendering. Now you can see why. React calls the component again from its first line, so any promise the component makes is made again too. Here the component calls load from data.js, which starts a new request every time, instead of fetchData:

A new promise on every render (wrong on purpose)Pop out in the code editor (opens in a new tab)JSX
import { Suspense, use } from "react";import { load } from "./data.js"; function OrderStatus() {  // Wrong on purpose: load makes a new promise every time this runs.  const order = use(load("/orders/42"));  return <p>Order {order.id}: {order.status}</p>;} export default function App() {  return (    <Suspense fallback={<p>Checking order 42…</p>}>      <OrderStatus />    </Suspense>  );}
ResultLoading…

The result says Checking order 42… forever, and the console's count of Load /orders/42 goes up about once a second. Line 6 makes promise A, and use suspends. A second later promise A is fulfilled, and React calls OrderStatus again. Line 6 runs again and makes promise B, which is pending, so use suspends again. The order is loaded over and over and never shown.

This page prints no warning, because each new promise takes a full second. React can only spot the mistake when it tries a component again straight away and gets a different promise than on the first try. That happens when the new promise settles almost at once, like use(fetchData("/orders/42").then((order) => order.status)): the cached order is there, but .then makes a new promise on every render. Then React prints this error in the console: A component was suspended by an uncached promise. Creating promises inside a Client Component or hook is not yet supported, except via a Suspense-compatible library or framework.

In plain words: this promise was made while the component rendered, so React can't keep it between renders. Unless you use a library or framework that supports Suspense, get the promise from a cache. Whether React warns or not, the fix is the same.

Top, load in the component: render 1 makes promise A. One second later, render 2 makes promise B, so use waits again and the fallback stays. Bottom, fetchData from a cache: render 1 gets promise A. One second later, render 2 gets promise A again, now fulfilled, and the order shows.load() in the component: a new promise each timerender 1promise A1 srender 2promise BPromise B starts the wait over, so the fallback stays.fetchData() from a cache: the same promiserender 1promise A1 srender 2promise ARender 2 reads promise A, now fulfilled: the order shows.
React calls the component again once promise A settles. With load in the component, that new call makes promise B, which hasn't settled, so use waits again and the fallback never goes away. With fetchData, the second call gets promise A back from the cache, and use returns the order.

The fix is to ask a cache. fetchData (lines 29 to 32 of data.js) keeps each promise in a Map, keyed by its url, and hands back the same promise on every call. Click Add tea after the order shows:

The same promise from a cachePop out in the code editor (opens in a new tab)JSX
import { Suspense, use, useState } from "react";import { fetchData } from "./data.js"; function OrderStatus() {  const order = use(fetchData("/orders/42"));  return <p>Order {order.id}: {order.status}</p>;} export default function App() {  const [cartCount, setCartCount] = useState(0);  return (    <>      <button onClick={() => setCartCount(cartCount + 1)}>Add tea ({cartCount})</button>      <Suspense fallback={<p>Checking order 42…</p>}>        <OrderStatus />      </Suspense>    </>  );}
ResultLoading…

Line 5 asks the cache, so every render of OrderStatus gets the same promise. Each click on line 13 re-renders App and OrderStatus, but the order stays on screen with no fallback: the promise is already fulfilled, so use returns its value at once. The console shows one Load /orders/42, however many times you click.

Can't a hook keep the promise instead? Not in the component that suspends. A component that suspends before it ever appears on screen hasn't been kept by React yet, so React throws away its state, its refs, and its useMemo, and starts it fresh on the next try:

useMemo in the component that suspends (wrong on purpose)JSX
function OrderStatus() {
  // Wrong on purpose: this component suspends before it ever appears,
  // so React keeps nothing for it, not even this useMemo.
  const promise = useMemo(() => load("/orders/42"), []);
  const order = use(promise);
  return <p>Order {order.id}: {order.status}</p>;
}

This is a piece of a component, not a whole example, so it doesn't run here; the useMemo in the same component button below runs it. Line 4 makes a new promise on every try, just like load on its own. The playground runs OrderStatus with its promise from four places and counts the loads:

Playground: where does the promise come from?
OrderStatus0 loads

Pick a button above.

What the page showednot started
    Try it yourself
    Pick a place

    Each button runs OrderStatus with its promise from a different place, for four seconds. The order takes one second to arrive.

    OrderStatus runs here, with React's real Suspense and use, in a root of its own. Each load is a pretend request that answers after 1 second; the counter goes up every time one starts. The page stops after 4 seconds, so a promise made in render can't load forever. Reset clears it.

    Where the promise should come from

    The rule from the last section is short: make the promise before the component that reads it renders, or keep it somewhere that outlives that component's renders. Three places work. The first is a cache, like fetchData. The second is an event handler: start the request when the reader clicks, and keep the promise in a parent's state.

    A promise made in a click handler and kept in statePop out in the code editor (opens in a new tab)JSX
    import { Suspense, use, useState } from "react";import { load } from "./data.js"; function Receipt({ receiptPromise }) {  const receipt = use(receiptPromise);  return <p>{receipt.items.join(" and ")}: ${receipt.total}</p>;} export default function App() {  const [receiptPromise, setReceiptPromise] = useState(null);  return (    <>      <button onClick={() => setReceiptPromise(load("/receipts/42"))}>Show receipt</button>      {receiptPromise && (        <Suspense fallback={<p>Loading the receipt…</p>}>          <Receipt receiptPromise={receiptPromise} />        </Suspense>      )}    </>  );}
    ResultLoading…

    Line 10 keeps receiptPromise in App's state, starting as null. The click on line 13 calls load once and saves the promise it returns. App never suspends (the boundary on line 15 is below it), so its state survives every render, and Receipt gets the same promise each time through its prop. Line 5 reads it. Click Show receipt: Loading the receipt…, then Tea and Cake: $7. Click it again: the click makes a new promise on purpose, a fresh request, so the fallback shows again for 0.8 seconds.

    The third place is the server. In a framework with Server Components, like Next.js, some components run on the server. One of them can start a request and pass the promise down without waiting for it. These two files show the idea; they need a framework, so they don't run here:

    OrderPage.jsx: a Server Component starts the requestJSX
    // OrderPage.jsx: a Server Component. It runs on the server.
    import { Suspense } from "react";
    import { db } from "./db.js";
    import OrderStatus from "./OrderStatus.jsx";
    
    export default function OrderPage() {
      // Start the request, but don't wait for it.
      const orderPromise = db.orders.find(42);
      return (
        <Suspense fallback={<p>Checking order 42…</p>}>
          <OrderStatus orderPromise={orderPromise} />
        </Suspense>
      );
    }
    OrderStatus.jsx: a Client Component reads itJSX
    // OrderStatus.jsx: a Client Component. It runs in the browser.
    "use client";
    import { use } from "react";
    
    export default function OrderStatus({ orderPromise }) {
      const order = use(orderPromise);
      return <p>Order {order.id}: {order.status}</p>;
    }

    Line 8 of OrderPage.jsx starts the request and keeps the promise, with no await, so the server can send the rest of the page at once. Line 11 passes it to OrderStatus, a Client Component (one that runs in the browser, marked by "use client" on line 2). Line 6 of OrderStatus.jsx reads it with use, and the fallback shows until the server sends the value. A Server Component itself would just await the promise. The Server Components lesson covers them.

    Three cards on the left: a cache, fetchData(url); a click, kept in state; a Server Component prop. Arrows lead from each to a function card, OrderStatus: use(promise). Below, a faded card, a new promise in render, has no arrow: it starts over on every render.a cache: fetchData(url)a click, kept in statea Server Component propOrderStatus: use(promise)a new promise in renderstarts over on every render
    The promise is made before OrderStatus renders, or kept somewhere that outlives its renders: a cache, a parent's state, or a Server Component. OrderStatus only reads it. A promise made inside its own render is new each time.

    Read the table row by row: where the promise is made, whether the next render gets the same one, and when to use it.

    Where the promise passed to use comes from
    Where the promise is madeSame promise on the next render?Use it when
    A cache, like fetchData(url)Yes. The cache hands back the promise it saved.Several components read the same data, or a framework or library gives you the cache.
    An event handler, then a parent's stateYes. State keeps it until you set a new one.The data is needed after a click, like a receipt.
    A Server Component, passed as a propYes. The server makes it once and sends it down.Your framework runs components on the server, like Next.js.
    The reading component's own renderNo. Every render makes a new one.Never. The fallback stays, and the request runs again and again.
    In a real app

    You rarely write fetchData yourself. Frameworks and data libraries keep the cache for you, decide when an answer is old, and fetch it again. The data.js tab shows the smallest version of that idea, so you can see what they do.

    A rejected promise goes to an error boundary

    Requests fail. When the promise passed to use is rejected, use throws its reason, the error the promise rejected with, just as a throw would. React passes it up to the nearest error boundary: a component that catches errors thrown while its children render and shows a fallback instead. The ErrorBoundary.jsx tab holds a small one; the next lesson explains how it works. The first stock check in data.js fails, like a busy server:

    A failed request caught by an error boundaryPop out in the code editor (opens in a new tab)JSX
    import { Suspense, use } from "react";import ErrorBoundary from "./ErrorBoundary.jsx";import { fetchData } from "./data.js"; function Stock() {  const left = use(fetchData("/stock/cake"));  return <p>Cake: {left} left</p>;} export default function App() {  return (    <>      <h3>Carrot cake, $4</h3>      <ErrorBoundary fallback={(error) => <p>Couldn't check the stock: {error.message}</p>}>        <Suspense fallback={<p>Checking the stock…</p>}>          <Stock />        </Suspense>      </ErrorBoundary>    </>  );}
    ResultLoading…

    Line 6 reads the stock. For 0.6 seconds, the Suspense boundary on line 15 shows Checking the stock…. Then the promise is rejected, use throws, and the ErrorBoundary on line 14 shows its fallback: Couldn't check the stock: The server is busy. Try again. The heading on line 13 stays, because it's outside the boundary. The console shows the error too: by default, React logs every error a boundary catches.

    The two boundaries work as a pair. Suspense handles not yet; the error boundary handles never. Put the error boundary outside the Suspense boundary, as lines 14 and 15 do, so one fallback covers the whole part that loads. The practical example below adds a Check again button.

    Another way: give the promise a value to fall back to

    If a failed request has a sensible stand-in, like an empty list of reviews, you can make the cache hand out a promise that never rejects: load(url).catch(() => []). Do it inside the cache, so that promise is made once too.

    use reads context too, even after an if

    The second resource use reads is a context. use(SomeContext) returns the value of the nearest provider above, the same value useContext(SomeContext) returns (Passing data deeply with context covers both). The difference is where you may call it. useContext is a hook, so it has to run at the top of the component, on every render (The rules of hooks). use may run later, inside an if:

    Read the currency only when there's a price to showPop out in the code editor (opens in a new tab)JSX
    import { createContext, use, useState } from "react"; const CurrencyContext = createContext("USD"); function Price({ amount, signedIn }) {  if (!signedIn) {    return <p>Sign in to see prices.</p>;  }  const currency = use(CurrencyContext);  return <p>Tea: {amount} {currency}</p>;} export default function App() {  const [signedIn, setSignedIn] = useState(false);  return (    <CurrencyContext value="EUR">      <button onClick={() => setSignedIn(!signedIn)}>{signedIn ? "Sign out" : "Sign in"}</button>      <Price amount={3} signedIn={signedIn} />    </CurrencyContext>  );}
    ResultLoading…

    Line 3 makes CurrencyContext, and line 16 provides "EUR" to everything inside it. In Price, lines 6 to 8 return early for a shopper who isn't signed in. Only after that does line 9 call use(CurrencyContext). With useContext, line 9 would break the rules of hooks, because it doesn't run on every render. Click Sign in: the price shows as Tea: 3 EUR. Click Sign out and it goes back to the message.

    A card at the top, CurrencyContext value="EUR". Below it two Price cards. The left one, signed in, has an arrow up to the context: it calls use(CurrencyContext) and shows Tea: 3 EUR. The right one, signed out, has no arrow: it returns before use and never reads the context.CurrencyContext value="EUR"Price signedIn={true}use(CurrencyContext)shows Tea: 3 EURPrice signedIn={false}returns before usenever reads the context
    Both Price components sit inside the provider. The signed-in one reaches use(CurrencyContext) and gets "EUR". The signed-out one returns before that line, so it never reads the context.

    Press Edit & run and change value="EUR" on line 16 to "GBP". After you sign in, the price reads Tea: 3 GBP. Like useContext, use looks for the nearest provider above the component; a provider that Price itself renders wouldn't count.

    Where you can and can't call use

    use isn't a hook, though its name starts with use. It has its own, shorter list of rules. Read the table row by row: the question, the answer for hooks, and the answer for use.

    Hooks and use: where each may be called
    QuestionHooks like useState and useContextuse
    Can you call it inside an if or a loop?No. A hook must run in the same order on every render.Yes. React doesn't need it at the same place on every render.
    Can you call it after an early return?No. The renders that return early would skip it.Yes. Price reads the currency after its early return.
    Can you call it inside try and catch?No. The rules of hooks forbid it.No. Your catch would catch React's signal, and React warns.
    Can you call it in an event handler or an Effect?No. Only while a component renders.No. Only while a component renders.
    Who may call it?Components and hooks.Components and hooks.

    One place is never allowed: inside try and catch. To suspend, use throws React's signal, and your catch would catch it before React does:

    use inside try and catch (wrong on purpose)Pop out in the code editor (opens in a new tab)JSX
    import { Suspense, use } from "react";import { fetchData } from "./data.js"; function OrderStatus() {  let status = "unknown";  try {    // Wrong on purpose: use must not sit inside try.    status = use(fetchData("/orders/42")).status;  } catch {    status = "unknown";  }  return <p>Order 42: {status}</p>;} export default function App() {  return (    <Suspense fallback={<p>Checking order 42…</p>}>      <OrderStatus />    </Suspense>  );}
    ResultLoading…

    The result shows Order 42: unknown straight away and keeps showing it. Line 8 threw the signal, line 9 caught it, and line 10 set unknown, so React never learned it should wait. The console shows React's warning: `use` was called from inside a try/catch block. To handle a failed promise, use an error boundary, as in the last section.

    One more rule: always hand the promise to use, even when you think it has settled. Don't read its status field yourself to skip the call. Those fields are React's bookkeeping, and skipping use hides the read from React and its developer tools.

    Skip use by reading the status (wrong on purpose)JSX
    // Wrong: reading the promise's status yourself to skip use.
    const order = orderPromise.status === "fulfilled" ? orderPromise.value : use(orderPromise);
    
    // Right: always hand the promise to use, and let React decide.
    const order = use(orderPromise);

    Line 2 is the wrong version, and line 5 the right one. These lines only show the pattern, so they don't run here. You may call use inside an if that depends on your props or state, as Price did; just don't make the choice depend on the promise itself.

    An order page that reads data three ways

    Here everything comes together: an order from the cache, a receipt from a click, a currency read only when needed, and a stock check that fails once and can be retried. Watch the result: the stock check fails first, then the order arrives.

    An order page with usePop out in the code editor (opens in a new tab)JSX
    import { Suspense, createContext, use, useState } from "react";import ErrorBoundary from "./ErrorBoundary.jsx";import { fetchData, load, refetchData } from "./data.js"; const CurrencyContext = createContext("USD"); function OrderStatus() {  const order = use(fetchData("/orders/42"));  return <p>Order {order.id}: {order.status}</p>;} function Receipt({ receiptPromise, showTotal }) {  const receipt = use(receiptPromise);  let total = "";  if (showTotal) {    const currency = use(CurrencyContext);    total = ` (${receipt.total} ${currency})`;  }  return <p>Receipt: {receipt.items.join(" and ")}{total}</p>;} function Stock() {  const left = use(fetchData("/stock/cake"));  return <p>Cake: {left} left</p>;} export default function App() {  const [receiptPromise, setReceiptPromise] = useState(null);  const [stockTries, setStockTries] = useState(1);   function checkStockAgain() {    refetchData("/stock/cake");    setStockTries(stockTries + 1);  }   return (    <CurrencyContext value="EUR">      <Suspense fallback={<p>Checking order 42…</p>}>        <OrderStatus />      </Suspense>      <button onClick={() => setReceiptPromise(load("/receipts/42"))}>Show receipt</button>      {receiptPromise && (        <Suspense fallback={<p>Loading the receipt…</p>}>          <Receipt receiptPromise={receiptPromise} showTotal={true} />        </Suspense>      )}      <ErrorBoundary        key={stockTries}        fallback={(error) => (          <p>            {error.message} <button onClick={checkStockAgain}>Check again</button>          </p>        )}      >        <Suspense fallback={<p>Checking the stock…</p>}>          <Stock />        </Suspense>      </ErrorBoundary>    </CurrencyContext>  );}
    ResultLoading…
    • Line 8: OrderStatus reads the order from the cache, inside its own boundary (lines 38 to 40).
    • Lines 12 to 20: Receipt reads the promise from its prop, then reads the currency with use inside the if on line 15. The click on line 41 makes that promise.
    • Line 23: Stock reads the stock from the cache. The first answer is a failure, so the ErrorBoundary on lines 47 to 58 shows the message and a Check again button.
    • Lines 31 to 34: checkStockAgain drops the failed promise from the cache with refetchData and adds one to stockTries. That number is the boundary's key on line 48, so React replaces the boundary with a fresh one, its error forgotten (Preserving and resetting state explains keys). Stock renders again, gets a new promise, and this time the server answers Cake: 3 left.

    Click Check again, then Show receipt. The receipt line ends with (7 EUR), the total in the currency from line 37.

    Same promise on every render, or a new one?
    • use(fetchData("/orders/42"))
    • use(load("/orders/42"))
    • use(fetchData("/orders/42").then((order) => order.status))
    • use(receiptPromise) // a prop, made in a parent's click handler
    • use(fetch("/api/orders/42").then((r) => r.json()))
    • use(menuPromise) // made once, at the top of the file
    • use(useMemo(() => load("/orders/42"), [])) // in the component that suspends
    • use(orderPromise) // a prop from a Server Component
    Try it yourself
    0 of 8 correct

    Each card is a use call inside a component. Sort it: does the component get the same promise every time it renders, or a new one?

    Choose a category for every card. You can change an answer at any time; Reset clears them all.

    Common mix-ups

    • "use is a hook, so the rules of hooks apply." No. use can be called inside if, loops, and after an early return. It still runs only while a component or hook renders, and never inside try.
    • "use waits for the promise, like await." No. It can't pause the function. It stops it, React shows a fallback, and React calls the component again from the top once the promise settles.
    • "I can create the promise right where I call use." No. A promise made while rendering is new on every render, so the fallback never goes away. Get it from a cache, a parent's state, or a Server Component.
    • "useMemo or useState in the same component keeps the promise." No. A component that suspends before it first appears is thrown away and started fresh, hooks and all. Keep the promise above it.
    • "Wrap use in try/catch to handle a failed request." No. That catches React's signal and breaks the wait. Put an error boundary around the component instead.
    • "use replaces Suspense." No. They work together: use suspends, and the nearest Suspense boundary decides what shows meanwhile.

    Practice exercises

    Each exercise has a live result: click in it, change the code with Edit & run, and open the console to see what data.js loads.

    Exercise 1 · Warm-upCount the loads

    Predict first: the order shows, and then you click Add tea three times. How many Load /orders/42 lines has the console printed in all? Then try it.

    import { Suspense, use, useState } from "react";import { fetchData } from "./data.js"; function OrderStatus() {  const order = use(fetchData("/orders/42"));  return <p>Order {order.id}: {order.status}</p>;} export default function App() {  const [cartCount, setCartCount] = useState(0);  return (    <>      <button onClick={() => setCartCount(cartCount + 1)}>Add tea ({cartCount})</button>      <Suspense fallback={<p>Checking order 42…</p>}>        <OrderStatus />      </Suspense>    </>  );}
    ResultLoading…

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 2 · Warm-upShow the heading straight away

      The page stays empty for a second, and then the heading and the order appear together. Make the heading show at once. With your fix, what does the page show during the first second?

      import { use } from "react";import { fetchData } from "./data.js"; function OrderStatus() {  const order = use(fetchData("/orders/42"));  return <p>Order {order.id}: {order.status}</p>;} export default function App() {  return (    <>      <h3>Your orders</h3>      <OrderStatus />    </>  );}
      ResultLoading…

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 3 · PracticeLoad the receipt once

        The receipt never shows. Fix it with one change. What does the page show once the receipt has arrived?

        import { Suspense, use } from "react";import { load } from "./data.js"; function Receipt() {  const receipt = use(load("/receipts/42"));  return <p>{receipt.items.join(" and ")}: ${receipt.total}</p>;} export default function App() {  return (    <Suspense fallback={<p>Loading the receipt…</p>}>      <Receipt />    </Suspense>  );}
        ResultLoading…

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 4 · PracticeRead the currency after the early return

          Show the currency after each price that the shopper is allowed to see. What does the Tea line show?

          import { createContext, use } from "react"; const CurrencyContext = createContext("USD"); function Price({ name, amount, signedIn }) {  if (!signedIn) {    return <p>{name}: sign in to see the price.</p>;  }  // Read the currency here, and show it after the amount.  return <p>{name}: {amount}</p>;} export default function App() {  return (    <CurrencyContext value="GBP">      <Price name="Tea" amount={3} signedIn={true} />      <Price name="Cake" amount={4} signedIn={false} />    </CurrencyContext>  );}
          ResultLoading…

          Answer, then press Check. Spacing and letter case don’t matter.

            Exercise 5 · ChallengeStart loading before the click

            The receipt starts loading only after the click, so it takes 0.8 seconds to appear. Make it start loading as soon as the pointer reaches the button. Then rest the pointer on the button for a second and click. Does the receipt appear at once, or after 0.8 seconds?

            import { Suspense, use, useState } from "react";import { fetchData } from "./data.js"; function Receipt() {  const receipt = use(fetchData("/receipts/42"));  return <p>{receipt.items.join(" and ")}: ${receipt.total}</p>;} export default function App() {  const [open, setOpen] = useState(false);  return (    <>      <button onClick={() => setOpen(true)}>Show receipt</button>      {open && (        <Suspense fallback={<p>Loading the receipt…</p>}>          <Receipt />        </Suspense>      )}    </>  );}
            ResultLoading…

            Answer, then press Check. Spacing and letter case don’t matter.

              Exercise 6 · ChallengeKeep the page when the stock check fails

              After 0.6 seconds the stock check fails and the whole page goes. Keep the page, and show a message in place of the stock line. With your fix, does the heading Carrot cake, $4 stay?

              import { Suspense, use } from "react";import ErrorBoundary from "./ErrorBoundary.jsx";import { fetchData } from "./data.js"; function Stock() {  const left = use(fetchData("/stock/cake"));  return <p>Cake: {left} left</p>;} export default function App() {  return (    <>      <h3>Carrot cake, $4</h3>      <Suspense fallback={<p>Checking the stock…</p>}>        <Stock />      </Suspense>    </>  );}
              ResultLoading…

              Answer, then press Check. Spacing and letter case don’t matter.

                Check your understanding

                The use quiz · 7 questionsScore: first tries count
                1. Question 1 of 7OrderStatus calls use(promise) while the promise is still pending. What happens?

                  Choose an answer to see the explanation.

                2. Question 2 of 7load starts a new request on every call. What does this page show five seconds later?

                  Read the code, then predictJSX
                  function OrderStatus() {
                    const order = use(load("/orders/42"));
                    return <p>Order 42: {order.status}</p>;
                  }
                  
                  // In App:
                  <Suspense fallback={<p>Checking order 42…</p>}>
                    <OrderStatus />
                  </Suspense>

                  Choose an answer to see the explanation.

                3. Question 3 of 7Does useMemo keep the same promise in this component?

                  Read the code, then predictJSX
                  function OrderStatus() {
                    // Wrong on purpose: this component suspends before it ever appears,
                    // so React keeps nothing for it, not even this useMemo.
                    const promise = useMemo(() => load("/orders/42"), []);
                    const order = use(promise);
                    return <p>Order {order.id}: {order.status}</p>;
                  }

                  Choose an answer to see the explanation.

                4. Question 4 of 7signedIn is false on one render and true on the next. Is this call to use allowed?

                  Read the code, then predictJSX
                  function Price({ amount, signedIn }) {
                    if (!signedIn) return <p>Sign in to see prices.</p>;
                    const currency = use(CurrencyContext);
                    return <p>Tea: {amount} {currency}</p>;
                  }

                  Choose an answer to see the explanation.

                5. Question 5 of 7The stock promise is rejected, and there's no error boundary anywhere. What does the reader see?

                  Read the code, then predictJSX
                  function Stock() {
                    const left = use(fetchData("/stock/cake")); // this promise rejects
                    return <p>Cake: {left} left</p>;
                  }
                  
                  // In App, with no error boundary anywhere:
                  <Suspense fallback={<p>Checking the stock…</p>}>
                    <Stock />
                  </Suspense>

                  Choose an answer to see the explanation.

                6. Question 6 of 7Why must use never sit inside try and catch?

                  Choose an answer to see the explanation.

                7. Question 7 of 7Which promise can a component safely pass to use?

                  Choose an answer to see the explanation.

                Key takeaways

                • use(promise) returns the value of a fulfilled promise, suspends while it's pending, and throws if it's rejected.
                • A Suspense boundary shows a fallback while the component waits; an error boundary shows one if the promise is rejected.
                • After a promise settles, React calls the component again from the top, so the promise must be the same one: from a cache, a parent's state, or a Server Component.
                • A promise made in render, even inside useMemo in the component that suspends, is new each time and never shows.
                • use(context) reads the nearest provider, like useContext, but may run after an early return or inside an if.
                • Call use only while rendering, never inside try, and never skip it by reading the promise's status.

                use reads a resource while rendering, and React handles the wait.
                Give it a promise that lasts, a Suspense boundary for the wait, and an error boundary for the failure.

                Coming next: Error boundaries. It shows how to write the ErrorBoundary this lesson borrowed, what it can and can't catch, how to retry, and how to report errors.

                CompleteFrontend Clear concepts. Working examples.