Real interfaces change with the data: show a login button or a welcome message, a spinner or the results, an error or the content. React has no special v-if or template language for this. You use ordinary JavaScript: if, the ternary operator ? : and the logical &&. This lesson shows when to choose each.
if statements and early returns
A component is a function, so it can use if before it returns. This is the clearest choice when the whole result differs:
function Greeting({ user }) {
if (!user) {
return <p>Please sign in.</p>;
}
return <p>Welcome back, {user.name}!</p>;
}
function App() {
return (
<div>
<Greeting user={null} />
<Greeting user={{ name: "Asha" }} />
</div>
);
}
export default App;Please sign in.
Welcome back, Asha!The first return is an early return. It deals with the special case first and exits, so the rest of the function can assume user exists. Early returns keep components flat: compare this with an if/else wrapped around everything.
A component can return null to render nothing at all, which is the standard way to hide it:
function Notice({ message }) {
if (!message) return null;
return <p style={{ background: "#ffd45c", padding: 6 }}>{message}</p>;
}
function App() {
return (
<div>
<Notice message="Backup finished." />
<Notice message="" />
<p>(The empty notice above renders nothing.)</p>
</div>
);
}
export default App;Backup finished.
(The empty notice above renders nothing.)The ternary operator
Inside JSX, you cannot write an if statement because JSX braces accept only expressions. The ternary operator condition ? a : b is an expression, so it fits:
function Status({ online }) {
return (
<p>
<span style={{ color: online ? "green" : "gray" }}>●</span>{" "}
{online ? "Online" : "Offline"}
</p>
);
}
function App() {
return (
<div>
<Status online={true} />
<Status online={false} />
</div>
);
}
export default App;● Online
● OfflineThe ternary can choose a value (color), a string, or whole chunks of JSX. For larger blocks, wrap them in brackets for readability:
function Profile({ user }) {
return (
<div>
{user ? (
<>
<h3>{user.name}</h3>
<p>{user.bio}</p>
</>
) : (
<p>No profile yet.</p>
)}
</div>
);
}
function App() {
return (
<div>
<Profile user={{ name: "Ravi", bio: "Loves databases." }} />
<Profile user={null} />
</div>
);
}
export default App;Ravi
Loves databases.
No profile yet.Avoid nesting ternaries inside ternaries: the result is hard to read. If you need three or more cases, use an if with early returns, a switch, or an object lookup (shown below).
Logical AND (&&)
When something should either appear or not (there is no "else" branch), use &&. If the left side is true, React renders the right side; if it is false, the expression is false, which renders nothing:
function Message({ unread, isAdmin }) {
return (
<div>
<p>Inbox</p>
{unread > 0 && <p>You have {unread} unread messages.</p>}
{isAdmin && <button>Admin panel</button>}
</div>
);
}
function App() {
return (
<div>
<Message unread={3} isAdmin={true} />
<hr />
<Message unread={0} isAdmin={false} />
</div>
);
}
export default App;Inbox
You have 3 unread messages.
Admin panel
InboxThe zero trap
&& returns its left side when it is falsy. If that left side is the number 0, React happily renders it:
function Bad({ count }) {
return <p>Bad: {count && <span>You have {count} items</span>}</p>;
}
function Good({ count }) {
return <p>Good: {count > 0 && <span>You have {count} items</span>}</p>;
}
function App() {
return (
<div>
<Bad count={0} />
<Good count={0} />
<Good count={2} />
</div>
);
}
export default App;Bad: 0
Good:
Good: You have 2 items"Bad: 0" is almost never what you want. Always make the left side a boolean: count > 0 &&, Boolean(count) && or !!count &&. The same trap applies to NaN and, for rendering "", nothing shows (an empty string renders as empty text).
Choosing between more than two cases
For a few states, an object works as a lookup table, and keeps the JSX tidy:
const MESSAGES = {
loading: "Loading...",
error: "Something went wrong.",
empty: "No results found.",
};
function Results({ status, items }) {
if (status !== "ready") {
return <p>{MESSAGES[status] ?? "Unknown state"}</p>;
}
return <ul>{items.map(item => <li key={item}>{item}</li>)}</ul>;
}
function App() {
return (
<div>
<Results status="loading" />
<Results status="error" />
<Results status="empty" />
<Results status="ready" items={["Apple", "Banana"]} />
</div>
);
}
export default App;Loading...
Something went wrong.
No results found.
Apple
BananaThis "status" pattern (loading, error, empty, ready) appears in nearly every screen that loads data, and you will use it again in the data-fetching lesson.
Conditionally choosing attributes and classes
The condition does not have to decide whether an element exists. It can change its props too:
function Task({ title, done }) {
return (
<li
className={done ? "task done" : "task"}
style={{ textDecoration: done ? "line-through" : "none", opacity: done ? 0.6 : 1 }}
>
{done ? "✔ " : "○ "}{title}
</li>
);
}
function App() {
return (
<ul style={{ listStyle: "none", padding: 0 }}>
<Task title="Write lesson" done={true} />
<Task title="Review lesson" done={false} />
</ul>
);
}
export default App;✔ Write lesson
○ Review lessonFor classes made of several optional parts, build the string from an array: ["btn", primary && "btn-primary"].filter(Boolean).join(" ") gives "btn btn-primary" or just "btn".
Hiding with CSS versus not rendering
Not rendering a component (&&, null) removes it from the page, and from React's memory: its state is discarded, and it is created afresh next time. Hiding with style={{ display: "none" }} or a class keeps the element and its state. Choose not rendering for most cases; use CSS hiding when you want to preserve state, such as the text typed into a collapsed form, or for animations.
Common mistakes
- Putting an
ifstatement inside JSX braces. Move it above thereturn, or use a ternary. - Writing
{count && <Item />}with a number on the left, which prints 0. - Nesting several ternaries until nobody can read them.
- Forgetting that
&&has no "else": use a ternary when both branches matter. - Returning
undefinedby forgettingreturnin a branch. React 19 acceptsundefinedand renders nothing, but it is usually a bug. - Using
display: noneand expecting the component's state to reset.
Exercise
- Write
Temperature({ celsius })that shows "Freezing" below 0, "Cold" below 15, "Warm" below 28, and "Hot" otherwise, using early returns. - Add a
loggedInprop toGreetingthat shows a "Sign out" button when true and a "Sign in" button when false. - Fix the zero trap in this expression:
{cart.length && <p>Cart ({cart.length})</p>}. - Extend the status lookup with a
"offline"message and a default for any unknown status.
Key points
- A component can return different JSX depending on its props and state; ordinary JavaScript decides which.
- Use if with early returns for large differences, the ternary operator for either-or inside JSX, and && when something should appear or not.
- null, false and undefined render nothing, so a component can return null to show nothing.
- Put a real boolean on the left of &&: with a number such as 0 on the left, React renders the 0.