React ยท Chapter 31 of 42

Loading and Error States

Good UX requires showing users what's happening: a loading indicator while data is fetched, and a clear error message if something goes wrong.

Manage these as explicit pieces of state alongside your data, rather than guessing from the data's presence.

Tracking states explicitly

Use separate state for `loading`, `error`, and `data` so your UI can render distinct views for each case.

Rendering the right UI

Check `loading` first, then `error`, then render the actual data โ€” this ordering keeps logic predictable.

Example 1 (jsx)
function Profile({ userId }) {
  const [state, setState] = useState({ loading: true, error: null, data: null });

  useEffect(() => {
    fetch(`/api/users/${userId}`)
      .then(res => res.json())
      .then(data => setState({ loading: false, error: null, data }))
      .catch(error => setState({ loading: false, error, data: null }));
  }, [userId]);

  if (state.loading) return <p>Loading...</p>;
  if (state.error) return <p>Error: {state.error.message}</p>;
  return <p>{state.data.name}</p>;
}
Output
Loading... then either an error message or the user's name

The component checks loading, then error, then renders data.

Key points

  • Track loading, error, and data as explicit state.
  • Check loading first, then error, then render data.
  • Clear feedback improves perceived performance and trust.
  • Avoid inferring loading/error purely from data being null.
๐Ÿ’ก Note: Skeleton loaders and spinners both work well; choose based on your design system.

๐Ÿ“ Quick Quiz

1. What three states should typically be tracked when fetching?

2. In what order should you typically check these states?

3. Why show a loading indicator?