2.5 Conditional Rendering
← Back to Chapter 2: React Fundamentals
In React, you often need to display different UI elements or components based on certain conditions (such as whether a user is logged in, whether data is still loading, or whether an error occurred).
1. Early Return Pattern (Guard Clauses)
If a component cannot render until certain conditions are met, use an early return before the main JSX:
function UserDashboard({ user, isLoading, error }) {
// 1. Loading state
if (isLoading) {
return <div className="skeleton-loader">Loading user profile...</div>;
}
// 2. Error state
if (error) {
return <div className="error-alert">Error: {error.message}</div>;
}
// 3. Unauthenticated state
if (!user) {
return <p>Please log in to continue.</p>;
}
// 4. Main happy path
return (
<div className="dashboard">
<h1>Welcome back, {user.name}!</h1>
</div>
);
}
Returning null to Prevent Rendering
If you want a component to hide itself completely, return null:
function WarningBanner({ show }) {
if (!show) return null; // Renders nothing to DOM
return <div className="warning">Caution: Session expiring soon!</div>;
}
2. Inline Ternary Operator (condition ? true : false)
For if-else branches inside JSX expressions, the ternary operator is the standard solution:
function AuthNav({ user, onLogout }) {
return (
<nav>
{user ? (
<button onClick={onLogout}>Sign Out ({user.name})</button>
) : (
<button onClick={() => navigate('/login')}>Sign In</button>
)}
</nav>
);
}
3. Logical AND Operator (condition && <Element />)
When you want to render an element only if a condition is true, and render nothing if false, use the logical && operator:
function NotificationBadge({ unreadCount }) {
return (
<div className="bell-icon">
🔔
{unreadCount > 0 && <span className="badge">{unreadCount}</span>}
</div>
);
}
0 TrapIn JavaScript, 0 && <Component /> evaluates to 0 (a falsy number), not false! Because 0 is a valid React text node, React will literally display a 0 on your screen!
const items = [];
// ❌ BUG: Renders "0" on screen!
<div>{items.length && <ItemList items={items} />}</div>
// ✅ FIX 1: Explicit boolean check
<div>{items.length > 0 && <ItemList items={items} />}</div>
// ✅ FIX 2: Boolean constructor or double negation
<div>{!!items.length && <ItemList items={items} />}</div>
4. Multi-Branch Rendering (Lookup Objects)
When an interface depends on an enum status ('idle' | 'loading' | 'success' | 'error'), avoid deeply nested ternaries. Use a lookup object or helper function:
function OrderStatusBadge({ status }) {
const BADGE_CONFIG = {
pending: { label: "Pending Payment", color: "amber" },
shipped: { label: "In Transit", color: "blue" },
delivered: { label: "Delivered", color: "emerald" },
cancelled: { label: "Cancelled", color: "rose" }
};
const current = BADGE_CONFIG[status] || { label: "Unknown", color: "slate" };
return <span className={`badge badge-${current.color}`}>{current.label}</span>;
}
5. Summary Checklist
- Use early returns (
if (...) return <Skeleton />) for clean component guards and loading/error states. - Return
nullwhen a component should hide itself without rendering anything to the DOM. - Use ternary operators (
condition ? <A /> : <B />) for two-way conditional branches inside JSX. - Use logical
&&for one-way rendering, but ensure numbers are compared as booleans (count > 0 && ...) to prevent accidental0text rendering. - Use lookup dictionaries instead of deeply nested ternaries for multi-state rendering.