# Conditional Rendering

Course: [React](https://www.devtutorials.ai/learn/react) · Section: Dynamic User Interfaces
Source: https://www.devtutorials.ai/learn/react/dynamic-ui/conditional-rendering

> Showing different things based on a condition: if statements, the ternary operator, the && operator, rendering based on state, and loading and error states.

## Why Conditional Rendering?

**Conditional rendering** means showing different things on the screen depending on a condition. For example, show "Log out" when the user is logged in, and "Log in" when they are not.

In React, you write conditions with normal JavaScript: `if`, the `? :` operator, and the `&&` operator. This lesson shows when to use each one.

### Real-World Example

A bookshop page checks the user and the data before it shows anything. While the books load, it shows "Loading…". If the list is empty, it shows "No books found".

A signed-in user sees **My orders** in the menu, and a guest sees **Sign in**. Each check is an `if`, `&&` or `? :` in the component, not a separate page.

<figure className="react-fig">
<svg viewBox="0 0 520 170" role="img" aria-labelledby="fig-cond-title" style={{ width: "100%", maxWidth: 520, height: "auto" }}>
  <title id="fig-cond-title">One condition, two possible screens: if isOpen is true show Open, otherwise show Closed</title>
  <rect x="185" y="14" width="150" height="40" rx="20" style={{ fill: "var(--accent-soft)", stroke: "var(--accent)", strokeWidth: 1.5 }} />
  <text x="260" y="39" textAnchor="middle" style={{ fill: "var(--accent)", fontSize: 14, fontWeight: 700, fontFamily: "var(--mono)" }}>isOpen ?</text>
  <path d="M220 54 L130 104" style={{ stroke: "var(--ok)", strokeWidth: 2, fill: "none" }} />
  <path d="M300 54 L390 104" style={{ stroke: "var(--bad)", strokeWidth: 2, fill: "none" }} />
  <text x="160" y="80" textAnchor="middle" style={{ fill: "var(--ok)", fontSize: 12, fontWeight: 600, fontFamily: "var(--sans)" }}>true</text>
  <text x="360" y="80" textAnchor="middle" style={{ fill: "var(--bad)", fontSize: 12, fontWeight: 600, fontFamily: "var(--sans)" }}>false</text>
  <rect x="60" y="104" width="140" height="46" rx="8" style={{ fill: "var(--ok-soft)", stroke: "var(--ok)", strokeWidth: 1.5 }} />
  <text x="130" y="133" textAnchor="middle" style={{ fill: "var(--ok)", fontSize: 16, fontWeight: 700, fontFamily: "var(--sans)" }}>Open</text>
  <rect x="320" y="104" width="140" height="46" rx="8" style={{ fill: "var(--bad-soft)", stroke: "var(--bad)", strokeWidth: 1.5 }} />
  <text x="390" y="133" textAnchor="middle" style={{ fill: "var(--bad)", fontSize: 16, fontWeight: 700, fontFamily: "var(--sans)" }}>Closed</text>
</svg>
<figcaption>One condition decides which of the two screens is shown.</figcaption>
</figure>

React has no special syntax for conditions. You use normal JavaScript inside your component.

## if Statements

Use an `if` statement **before** the `return`, to choose which TSX to return. This is best when the two screens are very different.

You cannot put `if` inside the TSX braces `{ }`. It must go before the `return`.

      <button onClick={() => setIsOpen(!isOpen)}>Change sign</button>
    </div>
  );
}
`,
    "App.css": `.open { color: #15803d; }
.closed { color: #dc2626; }
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

Look at `ShopSign`. If `isOpen` is `true`, the first `return` runs and the function stops there. If not, the second `return` runs.

### Returning null

A component can show nothing by returning `null`. This is useful when a component should hide itself.

```tsx
function SaleBanner({ onSale }: { onSale: boolean }) {
  if (!onSale) {
    return null; // show nothing
  }
  return <p>Big sale today!</p>;
}
```

## Ternary Operators

The **ternary operator** `? :` chooses between two values in one line. Because it gives back a value, you **can** use it inside the TSX braces `{ }`.

```tsx
condition ? valueIfTrue : valueIfFalse
```

Read it as: "Is the condition true? If yes, use the first value. If no, use the second value."

      <h2>{isLoggedIn ? "Welcome back, Asha!" : "Welcome, guest!"}</h2>

      <button onClick={() => setIsLoggedIn(!isLoggedIn)}>
        {isLoggedIn ? "Log out" : "Log in"}
      </button>

      {isLoggedIn ? (
        <p>You have 3 books in your cart.</p>
      ) : (
        <p>Log in to see your cart.</p>
      )}
    </div>
  );
}
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

Click the button. Three things change, and all three use `? :`: the heading text, the button text, and the paragraph.

When each part has more than one line of TSX, wrap it in round brackets `( )`, as in the last example.

## Logical AND Operator

Sometimes you want to show something **or nothing**. For this, use the `&&` operator.

```tsx
{condition && <p>Shown only when the condition is true</p>}
```

It means: if the condition is true, show the TSX after `&&`. If it is false, show nothing.

      <h3>
        Cart
        {cartCount > 0 && <span className="badge">{cartCount}</span>}
      </h3>
      {cartCount >= 3 && <p>You get free delivery!</p>}
      <button onClick={() => setCartCount(cartCount + 1)}>Add a book</button>{" "}
      <button onClick={() => setCartCount(0)}>Empty cart</button>
    </div>
  );
}
`,
    "App.css": `.badge {
  margin-left: 6px;
  background: #f57c00;
  color: #fff;
  border-radius: 999px;
  padding: 0 8px;
  font-size: 14px;
}
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

At the start, the cart count is 0, so the orange badge is hidden. Click **Add a book**. The badge appears. Click it three times, and the free delivery message appears too.

### Be Careful with Numbers

Do not put a plain number on the left of `&&`. If the number is `0`, React shows the `0` on the screen.

```tsx
{cartCount && <span>{cartCount}</span>}     // Wrong: shows "0" when the cart is empty
{cartCount > 0 && <span>{cartCount}</span>} // Right: shows nothing when the cart is empty
```

Use `? :` when you want to show one thing **or another thing**. Use `&&` when you want to show one thing **or nothing**.

## Rendering Based on State

Most conditions in React check **state**. When the state changes, React runs the component again and shows the matching screen.

A state can have more than two values. Here, `step` can be `"cart"`, `"address"` or `"done"`, so the component shows one of three screens.

      {step === "cart" && (
        <div>
          <h3>Your cart</h3>
          <p>Atomic Habits · ₹499</p>
          <button onClick={() => setStep("address")}>Next</button>
        </div>
      )}

      {step === "address" && (
        <div>
          <h3>Delivery address</h3>
          <p>12 MG Road, Pune</p>
          <button onClick={() => setStep("cart")}>Back</button>{" "}
          <button onClick={() => setStep("done")}>Place order</button>
        </div>
      )}

      {step === "done" && (
        <div>
          <h3>Thank you!</h3>
          <p>Your book is on its way.</p>
          <button onClick={() => setStep("cart")}>Start again</button>
        </div>
      )}
    </div>
  );
}
`,
    "App.css": `.box { border: 1px solid #cbd5e1; border-radius: 8px; padding: 4px 14px 14px; }
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

`type Step = "cart" | "address" | "done"` means `step` can only be one of these three words. If you make a spelling mistake, TypeScript warns you.

## Loading States

Getting data, for example from a server, takes time. While you wait, show a **loading state**: a message like "Loading..." so the user knows something is happening.

You keep a true/false state, such as `isLoading`:

1. Set `isLoading` to `true` when you start getting the data.
2. Show "Loading..." while it is `true`.
3. Set it to `false` when the data arrives, and show the data.

      <button onClick={loadBooks} disabled={isLoading}>Load books</button>
      {isLoading ? (
        <p className="loading">Loading...</p>
      ) : (
        <ul>
          {books.map((book) => <li key={book}>{book}</li>)}
        </ul>
      )}
    </div>
  );
}
`,
    "App.css": `.loading { color: #64748b; font-style: italic; }
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

Click **Load books**. "Loading..." shows for a moment, and then the list appears.

Look at `disabled={isLoading}` on the button. While loading, the button cannot be clicked again.

`fetchBooks` only pretends to talk to a server. It waits 1.5 seconds and then gives back the books. You will learn to get real data in a later lesson.

## Error States

Getting data can fail. For example, the internet may be down. When that happens, show an **error state**: a clear message that tells the user what went wrong.

Keep the error message in state. Use `null` to mean "no error".

      <button onClick={() => load(false)}>Load (works)</button>{" "}
      <button onClick={() => load(true)}>Load (fails)</button>

      {error && <p className="error">Error: {error}</p>}

      <ul>
        {books.map((book) => <li key={book}>{book}</li>)}
      </ul>
    </div>
  );
}
`,
    "App.css": `.error {
  color: #dc2626;
  background: #fef2f2;
  border: 1px solid #fecaca;
  border-radius: 6px;
  padding: 8px 12px;
}
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

Try both buttons. **Load (works)** shows the books. **Load (fails)** shows a red error message.

This component uses all three ways together:

- `if (isLoading)` returns a whole different screen while loading.
- `{error && ...}` shows the error message only when there is an error.
- The list shows the books, or nothing when the list is empty.

Most screens that get data need three states: **loading**, **error**, and **data**. Plan all three from the start.

## Common mistakes

<div className="tscroll">

| Mistake | What you see | Fix |
|---|---|---|
| Writing `if` inside `{ }` in TSX | `Expression expected.` | Use `? :` or `&&`, or move the `if` before `return` |
| `{count && <p>...</p>}` with a number | A `0` shows on the screen | Write `{count > 0 && <p>...</p>}` |
| Forgetting the `:` part of a ternary | Compile error | Always write both parts: `a ? b : c` |
| Returning `undefined` by forgetting `return` | Nothing shows | Return TSX or `null` from every path |
| Not turning `isLoading` back to `false` after an error | "Loading..." stays forever | Set `isLoading` to `false` after both success and failure |
| Not clearing the old error before trying again | The old error stays on screen | Call `setError(null)` when you start loading |
| Very long nested ternaries `a ? b : c ? d : e` | Code is hard to read | Use `if` before `return`, or split into small components |

</div>

## Check Yourself

## Exercises

## What to Remember

- Use `if` before `return` to choose between very different screens.
- Use `? :` to show one thing or another, and `&&` to show one thing or nothing.
- Screens that load data need three states: loading, error and data.

Next: List Rendering
