Introduction
Real screens are full of "it depends": show the login button or the user's name, a spinner while loading, an error if it failed. Conditional rendering is how a component chooses what to draw from the current data — React has no special template syntax for it, just ordinary JavaScript decisions placed where they fit. (Roman Urdu: Conditional rendering matlab — data dekh kar faisla, screen par kya dikhega aur kya nahi.)
Key points
- Four tools, four sizes:
- if / else with early returns — for whole-screen choices (loading screen vs content vs error). Return early, keep the main return clean.
- Ternary
cond ? A : B— for choosing between two pieces inside JSX. cond && A— for showing something or nothing.- A variable holding JSX — compute
let banner = …before the return when the choice has several branches.
- The
&&zero trap:{count && <p>…</p>}withcount = 0renders the number 0 on the page, because&&returns the falsy left side itself and React prints numbers. The fix: force a boolean —{count > 0 && …}. This is the video's interview-famous bug.(Roman Urdu: && ke saath 0 wali ghalti mashhoor hai — hamesha saaf boolean do, number nahi.) - The three-state screen is conditional rendering's home:
if (loading) …,if (error) …, then the data — exactly the shape of the Weather App (5.12) and every fetch screen after it. - Auth gates are the same idea:
user ? <Dashboard /> : <Login />— one ternary standing at the door of the whole app. - Return
nullto render nothing: a component (or branch) that returnsnullleaves no trace on the page — how optional banners and modals disappear cleanly. - Keep conditions readable: a ternary nested inside a ternary inside a ternary is a bug waiting for a reader. Past two levels, switch to early returns or a variable.
Code examples
Example 1 — The three-state screen with early returns
function UserProfile({ user, loading, error }) {
if (loading) return <p>Loading profile…</p>;
if (error) return <p className="error">Could not load the profile.</p>;
if (!user) return <p>No user selected.</p>;
return (
<div className="card">
<h3>{user.name}</h3>
<p>{user.email}</p>
</div>
);
}What it does: Each state gets its own early return, in the order a reader checks them: waiting, failed, empty — and only then the real content, with no nesting at all. This exact skeleton serves every data screen in the course.
What you will see: exactly one of the four outcomes at a time — the spinner text, the error, the empty note, or the profile card — never a mixture.
Example 2 — Ternary at the door: login vs dashboard
import { useState } from "react";
function Gate() {
const [user, setUser] = useState(null);
return (
<div>
{user ? (
<div>
<p>Welcome back, {user.name}!</p>
<button onClick={() => setUser(null)}>Log out</button>
</div>
) : (
<div>
<p>Please log in to continue.</p>
<button onClick={() => setUser({ name: "Ayesha" })}>Log in</button>
</div>
)}
</div>
);
}What it does: One ternary in the JSX chooses between the two faces of the app. Because it lives inside the return, the choice is re-evaluated on every render — log in and the whole face swaps, no page change involved.
What you will see: "Please log in to continue." with a Log in button; click it and the same spot becomes the welcome message with a Log out button.
Example 3 — The && shortcut and its 0 trap, wrong and right
function InboxBadges({ unread, offers }) {
return (
<div>
{/* WRONG: when unread is 0, the page prints "0" */}
{unread && <span className="badge">{unread} unread</span>}
{/* RIGHT: a real boolean guards the badge */}
{offers > 0 && <span className="badge">{offers} new offers</span>}
</div>
);
}What it does: The first badge uses the raw number as the guard — 0 && … evaluates to 0, and React happily renders that 0 as text. The second guard (offers > 0) is a true boolean, so nothing — not even a stray number — appears when there are no offers.
What you will see: with unread={0} and offers={2}, the buggy badge prints a bare "0" while the correct one shows "2 new offers". Give offers 0 too and only the stray "0" remains — the bug, isolated.
Quick recap
- Choose by size: early returns for whole screens, the ternary for A-or-B inside JSX,
&&for show-or-nothing, a JSX variable for many branches. - The loading–error–data trio and the login gate are the two shapes you will write forever.
- Guard
&&with a real boolean —{0 && …}prints 0 on the page.
Common mistakes
- The 0 badge —
{items.length && <List />}prints "0" for an empty list. The fix:{items.length > 0 && <List />}— always compare to a boolean with&&. - Ternary towers —
a ? … : b ? … : c ? …chains nobody can read or safely edit. The fix: past two branches, move the choice into early returns or an if-chain above the return. - Forgetting the third state — screens that handle data and loading but not error (or empty) crash or hang silently. The fix: every data screen gets all the returns — loading, error, empty, content — in that order.
Practice
- Build a
StockBadgecomponent receivingstockas a prop: above 10 show "In stock" (green), 1–10 show "Only {stock} left" (orange), 0 show "Sold out" (red). Use an if-chain with early returns, and test all three bands. - Build a
Notificationspanel: it receives an array of messages. Empty array → show "All caught up 🎉" (this is where the&&trap would print 0 — use the boolean guard); otherwise show the count line and the first message. Switch between the two by passing different arrays.