React Rendering Loading States

In the last lesson you learned about React Logical AND Rendering, so you can show something only when a value is true. This lesson uses that to fix the awkward wait before data arrives, by showing a “Loading…” message first and the real content once it is ready.

🤔 Why do we need a loading state?

Data takes time to arrive, and the user is staring at the screen the whole time. Here is the gap.

  • When your app asks a server for data, the answer does not come back right away. There is a small wait.
  • During that wait you have no data yet. So what do you put on the screen?
  • If you show an empty page, the user thinks the app is frozen or broken.
  • The fix is to show a friendly “Loading…” message while you wait, then show the real content once it arrives. That waiting message is the loading state.

So a loading state is just you telling the user “hold on, it is coming” instead of leaving them with a blank screen.

⏳ What is a loading state?

A loading state is a small piece of state that says “is the data ready yet?” and you render different things based on its answer.

  • You keep a boolean in state, usually called isLoading. When it is true, the data is still on the way. When it is false, the data is here.
  • While isLoading is true, you show a loading indicator, like the text “Loading…” or a spinner.
  • When isLoading turns false, you show the real content instead.
  • So the same component shows two different screens depending on one boolean. That is conditional rendering doing the work.

A simple way to picture it is a kettle. While the water heats, you wait. The moment it is ready, you pour the tea. The isLoading flag is just “is the kettle still heating?”.

It is one boolean

A loading state is nothing fancy. It is one true/false value in state that decides whether the screen shows “Loading…” or the real data.

🧩 The shape of the code

Let’s see the basic shape before any real example. You hold the flag in state and pick what to render from it.

  • Create the state with useState, starting at true because the data is not ready when the component first appears.
  • Read isLoading and choose what to return: the loading indicator while it is true, the content while it is false.
  • Later, when the data arrives, you call setIsLoading(false) to flip the flag.

Here is the shape. We are not fetching real data yet, just showing how the flag drives the screen.

import { useState } from "react";
function Profile() {
const [isLoading, setIsLoading] = useState(true); // not ready yet
return (
<div>
{isLoading ? <p>Loading...</p> : <p>Here is your profile!</p>}
</div>
);
}

So the ternary picks one of two screens. While isLoading is true, the user sees “Loading…”. The moment it becomes false, they see the profile.

💡 Simulating the wait with a button

Real data fetching comes in a later lesson, so let’s fake the wait here. A button will flip the flag from loading to loaded, just like real data arriving would.

  • We start isLoading at true, so the screen opens on “Loading…”.
  • A button calls setIsLoading(false) when clicked. Think of that click as the data finally showing up.
  • The moment the flag flips, React re-renders and swaps the loading text for the real content.

Here is the full component. Read the state first, then see how the button flips it.

import { useState } from "react";
function Messages() {
const [isLoading, setIsLoading] = useState(true);
return (
<div>
{isLoading ? (
<p>Loading your messages...</p>
) : (
<p>You have 3 new messages.</p>
)}
<button onClick={() => setIsLoading(false)}>
Pretend the data arrived
</button>
</div>
);
}

So at first you see “Loading your messages…”. Click the button and it switches to “You have 3 new messages.” That click is standing in for the real data coming back from a server.

Use a timer to feel a real wait

Instead of a button, you can call setTimeout(() => setIsLoading(false), 2000) to flip the flag after two seconds. It feels closer to a real network wait, and the conditional rendering pattern is exactly the same.

🚪 The early-return pattern

When the loading screen is very different from the loaded screen, a ternary gets hard to read. There is a cleaner way: return early.

  • Right at the top of the component, check the flag. If isLoading is true, return the spinner and stop there.
  • Because you already returned, the rest of the component only ever runs with real data. No nesting, no big ternary.
  • This keeps each screen in its own clean block, which is much easier to read as the content grows.

Here the same idea uses an early return instead of a ternary. The loading case leaves the function first.

import { useState } from "react";
function Dashboard() {
const [isLoading, setIsLoading] = useState(true);
if (isLoading) {
return <p>Loading...</p>; // leave early, show only the spinner
}
// from here down, the data is ready
return <h1>Welcome back, Alex!</h1>;
}

So if isLoading is true, the function returns the loading text and never reaches the welcome message. Once it is false, that first if is skipped and the real content runs. Same result as the ternary, just easier to read.

The early return must come first

Put the if (isLoading) return ... line before the code that uses the data. If you read the data above the check, your component might run with data that is not there yet and crash.

🔀 Loading, loaded, and error: three states

Real apps have one more case to plan for. The request can fail, so you usually think in three states, not two.

  • Loading: the data is on the way. Show the spinner.
  • Loaded: the data arrived fine. Show the content.
  • Error: the request failed, maybe the network dropped. Show a short “Something went wrong” message.

Here is how the three states map to what the user sees on screen.

State What it means What you show
Loading Data is still on the way A spinner or “Loading…”
Loaded Data arrived successfully The real content
Error The request failed A short error message

You will wire up the error part for real once you learn data fetching. For now, just know the third case exists so your screen never gets stuck or goes blank when something breaks.

✅ Best practices

A few small habits keep loading states clean and the user happy.

  • Start isLoading at true. The data is not ready when the component first appears, so the loading screen should show right away.
  • Name the flag clearly, like isLoading. A reader instantly knows it is a true/false switch.
  • Reach for the early-return pattern when the loading and loaded screens are very different. It reads better than a giant ternary.
  • Always plan for the error case too, even if you only show a simple message. A stuck screen feels worse than an honest “something went wrong”.

Give the user something to look at

Even a plain “Loading…” is far better than a blank page. The user knows the app is working and just needs a moment, instead of guessing whether it is broken.

🧩 What You’ve Learned

  • ✅ A loading state shows a waiting message while data is not ready, then the real content once it arrives
  • ✅ You hold the wait in a boolean state, usually called isLoading, starting at true
  • ✅ While isLoading is true you show a spinner or “Loading…”, and when it is false you show the content
  • ✅ Flipping the flag with setIsLoading(false) re-renders the component and swaps the screen
  • ✅ The early-return pattern (if (isLoading) return <Spinner />) keeps each screen in its own clean block
  • ✅ Real apps think in three states: loading, loaded, and error

Check Your Knowledge

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

  1. 1

    Why do you show a loading state instead of an empty screen?

    Why: Data does not arrive instantly. During the wait you have nothing to show, so a loading message tells the user to hold on instead of leaving them staring at a blank, broken-looking screen.

  2. 2

    What value should isLoading usually start at, and why?

    Why: When the component first shows up, the data has not arrived yet. So isLoading starts at true and the loading screen appears right away, then flips to false once the data is here.

  3. 3

    What does the early-return pattern look like for a loading state?

    Why: You check the flag at the top and return the spinner early. Because the function already returned, the rest of the component only runs once the data is ready, which keeps each screen clean.

  4. 4

    What are the three states a real data screen usually plans for?

    Why: Besides loading (data on the way) and loaded (data arrived), the request can fail. So you also plan for an error state and show a short message instead of leaving the screen stuck or blank.

🚀 What’s Next?

Now you can show a loading state while data is on the way and the real content once it arrives. But what if the data comes back and there is simply nothing in it, like an empty inbox? Next you’ll learn how to handle that gracefully.

React Rendering Empty States

Share & Connect