Skip to main content

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>
);
}
The Numeric Zero 0 Trap

In 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 null when 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 accidental 0 text rendering.
  • Use lookup dictionaries instead of deeply nested ternaries for multi-state rendering.