Conditional Rendering
Real interfaces change with the data. A "Log in" button turns into the user's name, an order shows "Shipped" or "Delivered", and an empty cart shows a friendly message instead of a list. Showing different JSX for different conditions is called conditional rendering.
React has no special syntax for this. You use ordinary JavaScript: if statements, the ternary operator ? : and the logical && operator. In this lesson you will learn when to use each one, how to avoid the famous "0 on the screen" bug, and how React 19.2's <Activity> component hides UI without losing its state.
if statements and early return
The simplest way is an if statement before the return. A component can return different JSX depending on its props, or return null to show nothing at all.
import { createRoot } from "react-dom/client";
function DeliveryStatus({ status, city }) {
if (status === "cancelled") {
return <p style={{ color: "crimson" }}>This order was cancelled.</p>;
}
if (status === "delivered") {
return <p style={{ color: "green" }}>Delivered in {city}. Enjoy!</p>;
}
if (!status) {
return null; // nothing to show yet
}
return <p>On the way to {city}: {status}</p>;
}
function Orders() {
return (
<>
<DeliveryStatus status="out for delivery" city="Pune" />
<DeliveryStatus status="delivered" city="Jaipur" />
<DeliveryStatus status="cancelled" city="Kochi" />
<DeliveryStatus city="Lucknow" />
</>
);
}
createRoot(document.getElementById("root")).render(<Orders />);The last order has no status, so its component returns null and nothing appears. Returning early keeps each case short and easy to read. Use if when the branches are large or completely different.
The ternary operator inside JSX
You cannot write an if statement inside curly braces, because braces need an expression. The ternary operator condition ? a : b is an expression, so it works anywhere in JSX. Use it when only a small part of the output changes.
The && operator is a shortcut for "show this or nothing". condition && <Thing /> gives <Thing /> when the condition is true, and false (which renders nothing) when it is false.
import { createRoot } from "react-dom/client";
function Inbox({ user, unread }) {
return (
<div style={{ borderBottom: "1px solid #ddd", padding: 8 }}>
<p>{user ? `Welcome back, ${user}` : "Welcome, guest"}</p>
{/* Bug: when unread is 0, React prints "0" */}
<p>Wrong: {unread && `You have ${unread} new messages`}</p>
{/* Fixed: the left side is now a real true/false value */}
<p>Right: {unread > 0 && `You have ${unread} new messages`}</p>
{!user && <button>Log in</button>}
</div>
);
}
createRoot(document.getElementById("root")).render(
<>
<Inbox user="Priya" unread={3} />
<Inbox user="Rohan" unread={0} />
<Inbox unread={0} />
</>
);Look at Rohan's box: the "Wrong" line shows a stray 0. The && operator returns its left side when that side is falsy, and 0 is a number, which React renders. This is the most common conditional-rendering bug. Make the left side a real boolean with a comparison (unread > 0), Boolean(unread) or !!unread.
Storing JSX in a variable
When the logic is too long for a ternary but you still want one return, prepare the varying part in a variable first. JSX is just a value, so this works naturally:
function SeatInfo({ seatsLeft }) {
let message;
if (seatsLeft === 0) {
message = <strong>Waiting list only</strong>;
} else if (seatsLeft < 10) {
message = <span>Hurry, only {seatsLeft} seats left</span>;
} else {
message = <span>Seats available</span>;
}
return (
<div className="seat-info">
<h3>Rajdhani Express, 3AC</h3>
{message}
</div>
);
}For many fixed cases, an object that maps each value to its label or colour is often cleaner than a long if chain: const labels = { placed: "Order placed", shipped: "Shipped" }; and then {labels[status]}.
Advanced: hiding UI without losing it
When a condition becomes false, React removes that part of the tree. Any state inside it, such as text typed into an input, is thrown away. Usually that is what you want. Sometimes it is not: switching between tabs should not erase a half-filled form.
React 19.2 added the <Activity> component for this case. With mode="hidden", React hides the children (using CSS display: none), keeps their state, and pauses their effects until they are visible again. Try typing in both tabs of each version below, then switch tabs:
import { useState, Activity } from "react";
import { createRoot } from "react-dom/client";
function NoteBox({ label }) {
return <textarea placeholder={`Type your ${label} notes...`} rows={3} cols={30} />;
}
function Tabs({ keepAlive }) {
const [tab, setTab] = useState("maths");
return (
<section style={{ marginBottom: 16 }}>
<h3>{keepAlive ? "With Activity" : "With && (state is lost)"}</h3>
<button onClick={() => setTab("maths")}>Maths</button>
<button onClick={() => setTab("physics")}>Physics</button>
<div>
{keepAlive ? (
<>
<Activity mode={tab === "maths" ? "visible" : "hidden"}><NoteBox label="maths" /></Activity>
<Activity mode={tab === "physics" ? "visible" : "hidden"}><NoteBox label="physics" /></Activity>
</>
) : (
<>
{tab === "maths" && <NoteBox label="maths" />}
{tab === "physics" && <NoteBox label="physics" />}
</>
)}
</div>
</section>
);
}
createRoot(document.getElementById("root")).render(
<>
<Tabs keepAlive={false} />
<Tabs keepAlive={true} />
</>
);The useState hook remembers which tab is open; you will study it properly in State with useState. In the first version, your maths notes disappear when you switch to physics and back. In the second, they are still there. Hidden activities still use memory, so use <Activity> for a few important panels, not for hundreds of items.
Common mistakes
- Writing
ifinside JSX braces.{if (x) {...}}is a syntax error. Use a ternary,&&, or move theifabove thereturn. - The
0bug.{items.length && <List />}shows0for an empty list. Write{items.length > 0 && <List />}. - Deeply nested ternaries.
a ? b : c ? d : einside JSX is hard to read. Useifstatements or a lookup object. - Returning
undefined. Forgettingreturnin one branch makes the component returnundefined. Returnnullon purpose when you want to show nothing.
Key points
- Conditional rendering uses plain JavaScript:
if,? :and&&. - A component can return early with different JSX, or return
nullto render nothing. - Inside JSX braces only expressions work, so use the ternary or
&&there. &&renders its left side when that side is falsy, so0appears on screen; compare with> 0.- Removing a component throws away its state;
<Activity mode="hidden">hides UI and keeps it.
Exercise
Write a ResultCard component with props name and marks (0 to 100). Show "Distinction" for 75 and above, "Pass" for 33 to 74 and "Fail" below 33, each in a different colour. If marks is not given, show "Result awaited". Also show a small "Topper!" badge only when marks are 95 or more.
Show solution
import { createRoot } from "react-dom/client";
function ResultCard({ name, marks }) {
if (marks === undefined) {
return <p>{name}: Result awaited</p>;
}
let grade = "Fail";
let colour = "crimson";
if (marks >= 75) {
grade = "Distinction";
colour = "green";
} else if (marks >= 33) {
grade = "Pass";
colour = "#087ea4";
}
return (
<p>
{name}: {marks} <span style={{ color: colour }}>{grade}</span>
{marks >= 95 && <strong> Topper!</strong>}
</p>
);
}
createRoot(document.getElementById("root")).render(
<>
<ResultCard name="Ananya" marks={97} />
<ResultCard name="Kabir" marks={58} />
<ResultCard name="Rohan" marks={21} />
<ResultCard name="Isha" />
</>
);The early return handles the missing result, variables hold the grade and colour, and marks >= 95 && is a true boolean, so no stray number can appear.
Quick Quiz
1. What does {count && <p>New</p>} show when count is 0?
&& returns the falsy left side, and React renders the number 0; use count > 0 && instead.
2. How can a component render nothing at all?
Returning null tells React to show nothing for that component.
3. Why can you not write an if statement inside JSX curly braces?
Use a ternary or && inside braces, or put the if before the return.
4. What happens to an input's typed text when {show && <Form />} becomes false?
Removing a component destroys its state; <Activity mode="hidden"> keeps it instead.
Learn React with a mentor
Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
