EduFlow
React Fundamentals
0%

Section 2 · Components & Props

Conditional Rendering

14 min3 min readCode playground

Lesson at a glance

Show, hide and switch UI based on data using early returns, the ternary operator and &&, while avoiding the stray “0” bug and unreadable nested conditions.

  1. 1Early return: whole alternatives
  2. 2Ternary: one thing or another
  3. 3&&: something or nothing
  4. 4Conditions in attributes
  5. 5Picking the right tool
  6. 6Not rendering versus hiding

No video here — just the real thing: a written lesson, runnable examples, a quiz and practice.

Real interfaces change shape with the data. The Watchlist shows a “Watched” badge on some movies but not others, an encouraging message when the list is empty, and a disabled Add button until you type a title. None of that needs special React syntax. It’s plain JavaScript deciding which JSX to return, and there are three tools for it.

Early return: whole alternatives

When a component shows something entirely different in one case, handle that case first and return:

src/components/MovieList.jsx
export default function MovieList({ movies }) {
  if (movies.length === 0) {
    return <p className="empty">Nothing to watch yet. Add a movie above.</p>;
  }

  return (
    <ul>
      {movies.map((movie) => (
        <li key={movie.id}>{movie.title}</li>
      ))}
    </ul>
  );
}

The empty case is dealt with in two lines, and the rest of the function only worries about the normal case. This is the most readable pattern, so reach for it first. (The map and key are the topic of the next lesson.)

A component can also return null to render nothing at all:

JSX
function WatchedBadge({ watched }) {
  if (!watched) return null;
  return <span className="badge">Watched</span>;
}

Ternary: one thing or another

Inside JSX, where you can’t write if, use the conditional operator for a choice between two outputs:

JSX
<p className="status">
  {movie.watched ? 'Watched' : 'To watch'}
</p>

It works for elements too: {isEditing ? <TitleInput /> : <h2>{title}</h2>}. Keep ternaries short. Once you nest one ternary inside another, or the branches run to several lines each, move the decision into a variable above the return, or into its own small component.

&&: something or nothing

When the alternative is “nothing”, the logical AND operator is shorter than a ternary with null:

JSX
<li>
  {movie.title}
  {movie.watched && <span className="badge">Watched</span>}
</li>

JavaScript evaluates a && b to a if a is falsy, otherwise to b. When movie.watched is false, the expression is false, and React renders nothing for false. When it’s true, you get the badge.

Conditions in attributes

Conditions aren’t only about whole elements. Attributes take expressions too:

JSX
<li className={movie.watched ? 'movie movie-watched' : 'movie'}>
  {movie.title}
</li>

<button disabled={title.trim() === ''}>Add</button>

<button aria-pressed={filter === 'watched'}>Watched</button>

The third one matters for accessibility: aria-pressed tells screen reader users which toggle button is active, which a color change alone doesn’t. When a value is false or null for a boolean attribute like disabled, React leaves it off the element.

Picking the right tool

All three patterns produce the same kind of result, so the choice is about readability. A rule of thumb that holds up well in code review:

  • The whole output changes (loading, error, empty, normal): early return, one case at a time, top to bottom.
  • One spot switches between two things: a ternary, kept to a line or two per branch.
  • One spot is either there or not: && with a real boolean on the left.

When you notice yourself writing a ternary inside a ternary, or an && chain with three conditions, stop and give the logic a name. Either compute a variable (const status = …) above the return, or extract a small component such as WatchedBadge that hides the decision inside itself. The parent then reads like a sentence: title, badge, actions.

Order matters with early returns, too. Put the cases that make the rest impossible first. If a movie list can be loading, failed or empty, check them in that order, so the final return only ever deals with real data.

Not rendering versus hiding

There’s an important difference between these two:

JSX
{showWatched && <WatchedPanel />}
<div hidden={!showWatched}>
  <WatchedPanel />
</div>

The first removes WatchedPanel from the tree when the condition is false. React unmounts it: its DOM goes away and any state inside it, such as text typed into an input, is destroyed. When it comes back, it starts fresh. The second keeps it mounted and only hides its wrapper (the HTML hidden attribute works like display: none), so the state survives.

Most of the time you want the first; there’s no reason to keep invisible UI alive. Choose hiding when the user expects their in-progress work inside the hidden part to still be there, as in tabs with half-filled forms.

Next, you’ll render the whole movie list from data with map, and learn why React keeps asking you for a key.

Key takeaways

  • Use an early return for whole alternative screens, a ternary for one of two things, and && for something or nothing.
  • A component can return null to render nothing at all.
  • {count && <List />} renders a literal 0 when count is 0; compare explicitly with count > 0 &&.
  • Conditions can drive attributes too, such as className, disabled and aria-pressed.
  • Not rendering a component removes it and its state; hiding it with CSS keeps both.

Further reading

Check your understanding

4 questions

Question 1 of 4

movies is an empty array. What does <div>{movies.length && <MovieList movies={movies} />}</div> show?

Question 2 of 4

MovieList should show a full-page “Nothing to watch yet” message when the list is empty, and the list otherwise. Which structure reads best?

Question 3 of 4

WatchedBadge should render nothing when watched is false. Which version does that?

Question 4 of 4

A collapsible “Watched” panel contains a text input. A user types a note, collapses the panel with {open && <Panel />}, and expands it again. What happened to the note?

Practice

Code playground

Badges and an empty state

MovieList has three bugs. Fix them so that:

  1. The Watched badge (<span className="badge">Watched</span>) appears only on movies whose watched is true.
  2. When movies is empty, the list renders the message Nothing to watch yet. instead of a list.
  3. The empty case never shows a stray 0.

App renders the list twice, once with three movies and once with none. Don’t change App.

function MovieList({ movies }) {
  return (
    <div>
      {movies.length && (
        <ul>
          {movies.map((movie) => (
            <li key={movie.id}>
              {movie.title}
              <span className="badge">Watched</span>
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

const MOVIES = [
  { id: 1, title: 'Arrival', watched: true },
  { id: 2, title: 'Past Lives', watched: false },
  { id: 3, title: 'Paddington 2', watched: false },
];

export default function App() {
  return (
    <main>
      <section id="full">
        <MovieList movies={MOVIES} />
      </section>
      <section id="empty">
        <MovieList movies={[]} />
      </section>
    </main>
  );
}
Loading the editor…

Ctrl/⌘ + Enter to run · Runs in a sandbox in your browser. Nothing leaves your device.

Preview

Console

(no output)

Checks

  • The full list still shows three movies
  • Arrival (watched) shows the Watched badge
  • Past Lives and Paddington 2 don’t show a badge
  • The empty list shows “Nothing to watch yet.” and no
    • No stray 0 appears for the empty list