Skip to content

React Tutorial Props and Data

Empty States and Conditional Lists in React

Lesson 41 of 156 2 min read By Deepak Paswan Updated Oct 10, 2026

Runnable lesson · press Try it on the examples to edit and run them here.

A list with zero items is a normal situation: a new user has no orders, a search finds nothing, a filter hides everything. If you ignore it, you show a blank area and the user wonders if the page is broken. A good empty state says what happened and what to do next.

The basic pattern

Check the length before you map:

App.jsx
JSX
function App() {
  const [tasks, setTasks] = React.useState([]);

  return (
    <div>
      {tasks.length === 0 ? (
        <p>You have no tasks yet.</p>
      ) : (
        <ul>
          {tasks.map((task) => (
            <li key={task.id}>{task.text}</li>
          ))}
        </ul>
      )}
      <button onClick={() => setTasks([...tasks, { id: tasks.length + 1, text: "Task " + (tasks.length + 1) }])}>
        Add a task
      </button>
    </div>
  );
}

An early return keeps JSX simple

When the empty case is the whole component, return early:

App.jsx
JSX
function ProductList({ products }) {
  if (products.length === 0) {
    return <p>No products found.</p>;
  }

  return (
    <ul>
      {products.map((p) => <li key={p.id}>{p.name}</li>)}
    </ul>
  );
}

function App() {
  return <ProductList products={[]} />;
}

Different reasons for “nothing”

“Nothing to show” has several causes, and each deserves its own message:

SituationBetter message
The user has never added anything“No tasks yet. Add your first task.” with a button
A search matched nothing“No results for “xyz”. Try another word.”
A filter hides everything“No items match these filters.” with a Clear filters button
Data is still loadingA loading indicator, not “No results” (see Loading States)
The request failedAn error message with Retry (see Error States)
App.jsx
JSX
function App() {
  const people = ["Ada", "Grace", "Linus", "Margaret"];
  const [query, setQuery] = React.useState("");

  const matches = people.filter((p) => p.toLowerCase().includes(query.toLowerCase()));

  return (
    <div>
      <input aria-label="Search" placeholder="Type to search" value={query} onChange={(e) => setQuery(e.target.value)} />
      {matches.length === 0 ? (
        <p>No results for "{query}". Try another word.</p>
      ) : (
        <ul>
          {matches.map((p) => <li key={p}>{p}</li>)}
        </ul>
      )}
    </div>
  );
}

Hide headers and wrappers with the list

If a list has a heading or a container that looks odd when empty, show it only when there is something in it:

App.jsx
JSX
function Comments({ list }) {
  if (list.length === 0) return null; // nothing at all, including no heading

  return (
    <section>
      <h3>Comments ({list.length})</h3>
      <ul>
        {list.map((c) => <li key={c.id}>{c.text}</li>)}
      </ul>
    </section>
  );
}

function App() {
  return (
    <div>
      <Comments list={[]} />
      <Comments list={[{ id: 1, text: "Nice!" }, { id: 2, text: "Thanks" }]} />
    </div>
  );
}

Always provide a safe value

A component that does items.map(...) crashes if items is undefined, which is common before data has arrived. Give it a default (items = []) or guard with items?.length. See Default Prop Values.

Accessibility

Screen readers do not see a blank space. A visible text message works for them as well. If the empty state appears after an action, such as filtering, consider announcing it with role="status" so assistive technology reads it.

Key points

  • Check for zero items before rendering and show a helpful message.
  • Distinguish “never had any”, “no matches”, “loading” and “failed”.
  • Return null or hide wrappers and headings when there is nothing to list.