Conditional Rendering

Rendering different UI based on conditions

What Is Conditional Rendering?

Conditional rendering in React lets you render different UI elements based on conditions—just like using if statements in JavaScript. You can show, hide, or switch between components depending on your application's state.

Key Concept

Since JSX is JavaScript, you can use any JavaScript expression for conditional rendering. The most common patterns are: ternary operators, logical && operator, and early returns.

Ternary Operator (? :)

Best for choosing between two different elements:

function Greeting({ isLoggedIn }) {
  return (
    <div>
      {isLoggedIn ? (
        <h1>Welcome back!</h1>
      ) : (
        <h1>Please sign in.</h1>
      )}
    </div>
  );
}

// Inline for simpler cases
function Status({ isOnline }) {
  return (
    <span className={isOnline ? 'text-green-500' : 'text-gray-500'}>
      {isOnline ? '● Online' : '○ Offline'}
    </span>
  );
}

// Nested ternary (use sparingly!)
function Badge({ role }) {
  return (
    <span>
      {role === 'admin' ? '👑 Admin' : 
       role === 'mod' ? '🛡️ Moderator' : 
       '👤 User'}
    </span>
  );
}

Logical AND Operator (&&)

Best for showing something or nothing:

function Notification({ count }) {
  return (
    <div>
      {/* Only shows if count > 0 */}
      {count > 0 && (
        <span className="badge">{count} new messages</span>
      )}
    </div>
  );
}

function UserProfile({ user }) {
  return (
    <div>
      <h2>{user.name}</h2>
      
      {/* Show only if admin */}
      {user.isAdmin && (
        <span className="admin-badge">Admin</span>
      )}
      
      {/* Show only if bio exists */}
      {user.bio && (
        <p className="bio">{user.bio}</p>
      )}
      
      {/* Show only if has posts */}
      {user.posts.length > 0 && (
        <PostList posts={user.posts} />
      )}
    </div>
  );
}

⚠️ Watch Out for 0

{count && <span>{count}</span>} will render "0" when count is 0!
Use {count > 0 && ...} or ternary instead.

Early Return Pattern

Return early to avoid nested conditions:

function UserDashboard({ user, isLoading, error }) {
  // Handle loading state
  if (isLoading) {
    return <LoadingSpinner />;
  }

  // Handle error state
  if (error) {
    return <ErrorMessage message={error} />;
  }

  // Handle no user
  if (!user) {
    return <p>No user found.</p>;
  }

  // Main content (happy path)
  return (
    <div>
      <h1>Welcome, {user.name}!</h1>
      <UserStats stats={user.stats} />
    </div>
  );
}

Conditional CSS Classes

function Button({ variant, isDisabled, isLoading }) {
  // Template literals
  const className = `
    btn 
    btn-${variant} 
    ${isDisabled ? 'btn-disabled' : ''} 
    ${isLoading ? 'btn-loading' : ''}
  `;

  return <button className={className}>Click</button>;
}

// Using array join
function Card({ isActive, isHighlighted }) {
  const classes = [
    'card',
    isActive && 'card-active',
    isHighlighted && 'card-highlighted'
  ].filter(Boolean).join(' ');

  return <div className={classes}>...</div>;
}

// With clsx/classnames library (popular)
import clsx from 'clsx';

function MenuItem({ isActive, isDisabled }) {
  return (
    <li className={clsx(
      'menu-item',
      isActive && 'menu-item-active',
      isDisabled && 'menu-item-disabled'
    )}>
      Menu Item
    </li>
  );
}

Conditional Attributes

function Input({ isRequired, isDisabled, maxLength }) {
  return (
    <input
      type="text"
      required={isRequired}
      disabled={isDisabled}
      maxLength={maxLength || undefined}  // Only set if exists
    />
  );
}

// Spread conditional props
function Button({ isPrimary, ...props }) {
  const conditionalProps = isPrimary 
    ? { className: 'btn-primary', 'data-variant': 'primary' }
    : { className: 'btn-secondary' };

  return <button {...conditionalProps} {...props} />;
}

Switch-Like Rendering

// Using object lookup (recommended)
function Icon({ type }) {
  const icons = {
    home: <HomeIcon />,
    user: <UserIcon />,
    settings: <SettingsIcon />,
    default: <DefaultIcon />
  };

  return icons[type] || icons.default;
}

// Using a mapping object for pages
function Page({ page }) {
  const pages = {
    home: HomePage,
    about: AboutPage,
    contact: ContactPage
  };

  const Component = pages[page] || NotFoundPage;
  return <Component />;
}

// Using switch (in a helper function)
function getStatusMessage(status) {
  switch (status) {
    case 'loading': return 'Loading...';
    case 'success': return 'Data loaded!';
    case 'error': return 'Something went wrong';
    default: return 'Unknown status';
  }
}

function StatusDisplay({ status }) {
  return <p>{getStatusMessage(status)}</p>;
}

Rendering null

Return null to render nothing:

function WarningBanner({ warning }) {
  // Render nothing if no warning
  if (!warning) {
    return null;
  }

  return (
    <div className="warning-banner">
      ⚠️ {warning}
    </div>
  );
}

// In JSX with ternary
function OptionalSection({ showSection, content }) {
  return (
    <div>
      <h1>Main Content</h1>
      {showSection ? <section>{content}</section> : null}
    </div>
  );
}

Real-World Example: Loading States

import { useState, useEffect } from 'react';

function DataFetcher() {
  const [data, setData] = useState(null);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetch('/api/data')
      .then(res => res.json())
      .then(data => {
        setData(data);
        setIsLoading(false);
      })
      .catch(err => {
        setError(err.message);
        setIsLoading(false);
      });
  }, []);

  if (isLoading) {
    return (
      <div className="loading">
        <Spinner />
        <p>Loading data...</p>
      </div>
    );
  }

  if (error) {
    return (
      <div className="error">
        <p>Error: {error}</p>
        <button onClick={() => window.location.reload()}>
          Retry
        </button>
      </div>
    );
  }

  return (
    <div className="data">
      <h2>Data Loaded!</h2>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  );
}

🎯 Conditional Rendering Best Practices

  • ✓ Use ternary for if-else (two options)
  • ✓ Use && for show-or-hide (one option)
  • ✓ Use early returns to flatten nested conditions
  • ✓ Use object lookup for switch-like logic
  • ✓ Be careful with && and falsy values (0, '')
  • ✓ Extract complex conditions into variables or functions
  • ✓ Consider using clsx/classnames for conditional classes