Runnable lesson · press Try it on the examples to edit and run them here.
Real interfaces change with data: a logged-in user sees a different menu, an empty cart shows a message, an error shows a warning. In React you do this with plain JavaScript conditions.
1. An if statement before return
Best when the whole component should look different:
function Greeting({ loggedIn }) {
if (loggedIn) {
return <p>Welcome back!</p>;
}
return <p>Please log in.</p>;
}
function App() {
const [loggedIn, setLoggedIn] = React.useState(false);
return (
<div>
<Greeting loggedIn={loggedIn} />
<button onClick={() => setLoggedIn(true)}>Log in</button>
</div>
);
}
A component can return null to render nothing.
2. The ternary operator
Best for choosing between two pieces of JSX:
function App() {
const [on, setOn] = React.useState(false);
return (
<div>
<p>Switch is {on ? "On" : "Off"}</p>
<button onClick={() => setOn(!on)}>Toggle</button>
</div>
);
}
3. Logical AND (&&): show or nothing
Best when there is no “else” branch:
function App() {
const [items, setItems] = React.useState(2);
return (
<div>
<p>Items in cart: {items}</p>
{items > 0 && <button>Checkout</button>}
<button onClick={() => setItems(0)}>Empty cart</button>
</div>
);
}
Always make the left side a real boolean (items > 0, not just items), or React will print a stray 0 when the number is zero. See Rendering Dynamic Values.
4. Choose among many with an object or switch
Nested ternaries get hard to read. For three or more cases, use a lookup object or a switch in a helper:
const views = {
loading: <p>Loading...</p>,
success: <p>Data loaded</p>,
error: <p>Something went wrong</p>,
};
function App() {
const order = ["loading", "success", "error"];
const [index, setIndex] = React.useState(0);
const status = order[index];
return (
<div>
{views[status]}
<button onClick={() => setIndex((index + 1) % order.length)}>Next</button>
</div>
);
}
5. Store JSX in a variable
When a condition needs several lines, set a variable first:
function App() {
const role = "admin";
let panel = null;
if (role === "admin") {
panel = <p>You are an admin</p>;
} else if (role === "editor") {
panel = <p>You are an editor</p>;
}
return <div>{panel}</div>;
}
Not rendering vs hiding with CSS
{show && <Panel />} removes the component from the page, and it loses its state. Hiding with CSS (style={{ display: show ? "block" : "none" }}) keeps it mounted and keeps its state. Pick on purpose:
- Not rendering: cheaper, resets state, nothing for screen readers to find.
- CSS hiding: keeps state (for example text typed in a form), costs memory.
Common mistakes
{count && <X />}with a number. Usecount > 0 &&.- Nested ternaries three levels deep. Switch to a lookup object or early returns.
- Returning
undefined. Forgettingreturnshows nothing and logs an error. Returnnullon purpose. - Changing the order of hooks inside a condition. Never call hooks conditionally; see Rules of Hooks.
Key points
- Use
if/early return, the ternary,&&and lookup objects; it is all plain JavaScript. - Return
nullto render nothing. - Make
&&conditions real booleans.
Exercise: Show a warning only when the password is short
Make a password input. When the text is shorter than 8 characters (and not empty), show “Too short”. —solution
function App() {
const [password, setPassword] = React.useState("");
const tooShort = password.length > 0 && password.length < 8;
return (
<div>
<label>
Password <input aria-label="Password" type="text" value={password} onChange={(e) => setPassword(e.target.value)} />
</label>
{tooShort && <p style={{ color: "#c0392b" }}>Too short</p>}
</div>
);
}