Empty States: Helpful Defaults When There's Nothing to Show
Replace blank screens with guidance that explains what to do next.
The empty state is what a user sees when there’s no data yet. It’s often an afterthought, rendered as nothing at all, which is exactly when a new user is most lost and most in need of a nudge.
A blank list answers none of the user’s questions. Why is it empty? Is something broken? What am I supposed to do? A good empty state answers all three in one line.
Rendering Nothing
The default behavior, silence:
import { useState } from "react";
function App() {
const [todos, setTodos] = useState([]);
return (
<div>
<h1>Empty State Pattern</h1>
{todos.length === 0 ? null : (
<ul>{todos.map((todo, i) => <li key={i}>{todo}</li>)}</ul>
)}
</div>
);
}
export default App;
Say Something Useful
A dedicated empty component with copy that moves the user forward:
import { useState } from "react";
function EmptyTodos() {
return <p>No todos yet. Click the button to add one.</p>;
}
function App() {
const [todos, setTodos] = useState([]);
const [newTodo, setNewTodo] = useState("");
const handleAdd = () => {
if (newTodo.trim()) {
setTodos([...todos, newTodo]);
setNewTodo("");
}
};
return (
<div>
<h1>Empty State Pattern</h1>
<div>
<input
type="text"
value={newTodo}
onChange={(e) => setNewTodo(e.target.value)}
placeholder="Add a todo..."
/>
<button onClick={handleAdd}>Add Todo</button>
</div>
{todos.length === 0 ? <EmptyTodos /> : (
<ul>
{todos.map((todo, index) => <li key={index}>{todo}</li>)}
</ul>
)}
</div>
);
}
export default App;
The strongest empty states pair the explanation with the primary action itself, an “Add Todo” button right there, so the user doesn’t have to hunt for it. Keep the copy short and encouraging, and never worded like the user’s fault. An illustration can soften the moment, but only when it adds clarity rather than decoration.