React Displaying API Data

In the last lesson, React API Error Handling, you learned to handle loading, errors, and success. This lesson is about that success case: how to display API data well, turning a messy JSON response into a clean, safe layout.

🤔 Why does displaying data need its own lesson?

Here’s the pain. Once the data arrives, showing it isn’t always as simple as {user.name}. Real responses have quirks.

  • The data is an array, so you need to map it into elements with a key.
  • Fields are often nested, like user.address.city, several levels deep.
  • Some values are missing or null, and reading into them can crash the page.
  • The array might come back empty, and a blank screen looks broken.

So this lesson is about turning messy real data into a clean display, without your code crashing on the rough edges.

💡 Map the array into cards

The core move is the same map you already know. You take the array in state and turn each item into a small piece of UI, like a card.

function UserList({ users }) {
return (
<div className="user-list">
{users.map((user) => (
<div className="card" key={user.id}>
<h3>{user.name}</h3>
<p>{user.email}</p>
</div>
))}
</div>
);
}

This is the bread and butter of displaying data.

  • users.map(...) turns each user object into a <div> card.
  • Each card needs a key, and user.id is the perfect stable key.
  • Inside the card you pick the fields you want to show, like name and email.

So whenever the API gives you an array, map is how you turn it into a list of elements on screen.

🪜 Reaching nested fields safely

Real data nests. A user might have an address object inside, with a city inside that. Reading deep is fine, until a field is missing and your code crashes.

// the data shape:
// { id: 1, name: "Alex", address: { city: "London" } }
// ❌ if address is missing, this crashes: "cannot read city of undefined"
<p>{user.address.city}</p>
// ✅ optional chaining: if address is missing, this just gives undefined
<p>{user.address?.city}</p>

Here’s why the safe version matters.

  • user.address.city assumes address always exists. If it’s missing, reading .city on undefined throws and the page breaks.
  • The ?. is optional chaining. It stops safely and returns undefined if the part before it is missing.
  • So user.address?.city either gives the city, or quietly gives nothing, but never crashes.

Real data is messy

Don’t assume every field is always there. APIs leave fields out, send null, or change shape. Use optional chaining (?.) when reaching into nested objects, so one missing field doesn’t take down the whole page.

🩹 Show a fallback for missing values

Optional chaining stops crashes, but undefined on screen looks bad. So give missing values a friendly fallback using ||.

<h3>{user.name || "Unknown user"}</h3>
<p>{user.address?.city || "No city listed"}</p>

This little pattern keeps the display tidy.

  • user.name || "Unknown user" shows the name if it exists, or the fallback text if it’s empty or missing.
  • user.address?.city || "No city listed" does the same for the nested city.
  • So instead of a blank or undefined, the user always sees something readable.

Combine ?. to avoid crashes with || to fill in a sensible default, and your display stays safe and clean.

📭 Handle an empty list

Sometimes the request works but the array is empty, like a search with no matches. A blank screen looks like a bug, so show an empty state.

function UserList({ users }) {
if (users.length === 0) {
return <p>No users found.</p>;
}
return (
<div>
{users.map((user) => (
<div key={user.id}>{user.name}</div>
))}
</div>
);
}

Notice the small check at the top.

  • users.length === 0 is true when the array came back empty.
  • In that case we return a clear “No users found.” message instead of an empty <div>.
  • Otherwise we fall through and map the list as usual.

So now you handle four cases in total: loading, error, empty, and a full list. Each one shows something clear, never a confusing blank.

🖼️ Showing images from the data

Often the data includes an image URL. You display it by passing that URL into an <img> tag’s src.

{products.map((product) => (
<div key={product.id}>
<img src={product.image} alt={product.name} width="120" />
<h3>{product.name}</h3>
</div>
))}

A couple of points on images.

  • src={product.image} uses the URL straight from the data.
  • Always add an alt describing the image, both for accessibility and if the image fails to load.
  • If an image URL might be missing, give a fallback, like src={product.image || "/placeholder.png"}.

So images are just another field. Read the URL from the data and drop it into src.

⚠️ Common Mistakes

The most common mistake is reaching into nested data without checking, so one missing field crashes everything.

// ❌ crashes the whole list if any user has no company
{users.map((u) => <p key={u.id}>{u.company.name}</p>)}
// ✅ safe with optional chaining and a fallback
{users.map((u) => <p key={u.id}>{u.company?.name || "No company"}</p>)}

Keep these in mind.

  • Don’t read nested fields blindly. Use ?. so a missing object doesn’t crash the page.
  • Don’t forget the key on mapped elements. Use a stable id, not the array index when you can.
  • Don’t ignore the empty case. An empty array should show a message, not a blank space.

✅ Best Practices

A few habits for displaying API data cleanly.

  • map arrays into elements, always with a stable key like an id.
  • Use optional chaining (?.) for nested fields, and || for friendly fallbacks.
  • Handle the empty array with a clear “nothing here” message.
  • Add alt text to images, and a placeholder when a URL might be missing.
  • Show only the fields you need, not the whole raw object.

Inspect the shape first

Before writing the display, console.log the data once and look at its real shape. Knowing exactly where city or image lives saves you from guessing and from crashes on fields that aren’t where you expected.

🧩 What You’ve Learned

  • ✅ Use map to turn an array of results into elements, each with a stable key
  • ✅ Real data nests, so reach deep fields with optional chaining (?.) to avoid crashes
  • ✅ Use || to show a friendly fallback when a value is missing
  • ✅ Handle an empty array with a clear “nothing found” message
  • ✅ Show images by putting the data’s URL into an <img src>, always with alt text
  • ✅ Inspect the data shape first so you know exactly where each field lives

Check Your Knowledge

Test what you learned. Pick an answer for each question, then click Check.

  1. 1

    How do you turn an array of API results into elements on screen?

    Why: map turns each item into an element. Each one needs a stable key, like an id, so React can track the items correctly.

  2. 2

    Why use optional chaining (?.) when reading nested fields?

    Why: If a nested object like address is missing, reading .city on undefined throws. Optional chaining stops safely and returns undefined instead of crashing.

  3. 3

    How do you show a friendly value when a field is missing?

    Why: The || operator shows the value if it exists, or the fallback text if it's missing or empty, so the user never sees a blank or undefined.

  4. 4

    What should you show when the API returns an empty array?

    Why: An empty array is a valid success, not an error. Show a clear message so the user knows there's simply nothing to display, rather than a confusing blank screen.

🚀 What’s Next?

Now you can display real API data cleanly, even when it’s nested or missing fields. Next we’ll make it interactive by adding a search box that filters the API results as the user types.

React Search Functionality with APIs

Share & Connect