Skip
Arish's avatar

14. Conditional Rendering


Conditional Rendering

React lets you render different UI based on conditions. There are several patterns for conditional rendering.

If Statements

Use regular JavaScript if statements:

jsx
1function Greeting({ isLoggedIn }) {
2  if (isLoggedIn) {
3    return <h1>Welcome back!</h1>
4  }
5  return <h1>Please sign in</h1>
6}

Early Returns

Clean pattern for handling special cases:

jsx
1function UserProfile({ user, loading, error }) {
2  if (loading) {
3    return <Spinner />
4  }
5  
6  if (error) {
7    return <ErrorMessage message={error} />
8  }
9  
10  if (!user) {
11    return <p>No user found</p>
12  }
13  
14  // Main render
15  return (
16    <div>
17      <h1>{user.name}</h1>
18      <p>{user.email}</p>
19    </div>
20  )
21}

Ternary Operator

For inline conditional rendering:

jsx
1function Greeting({ isLoggedIn }) {
2  return (
3    <div>
4      {isLoggedIn ? (
5        <UserDashboard />
6      ) : (
7        <LoginForm />
8      )}
9    </div>
10  )
11}
12
13// Simple values
14function Status({ isOnline }) {
15  return (
16    <span className={isOnline ? 'online' : 'offline'}>
17      {isOnline ? 'Online' : 'Offline'}
18    </span>
19  )
20}

Logical AND (&&)

Render something OR nothing:

jsx
1function Notifications({ messages }) {
2  return (
3    <div>
4      {messages.length > 0 && (
5        <span className="badge">{messages.length} new</span>
6      )}
7    </div>
8  )
9}
10
11// ⚠️ Beware of falsy values
12function Counter({ count }) {
13  // ❌ Bug: renders "0" when count is 0
14  return <div>{count && <span>Count: {count}</span>}</div>
15  
16  // ✅ Fix: use comparison
17  return <div>{count > 0 && <span>Count: {count}</span>}</div>
18}

Logical OR (||) and Nullish Coalescing (??)

Default values:

jsx
1function Welcome({ name }) {
2  // Show default if name is falsy
3  return <h1>Hello, {name || 'Guest'}!</h1>
4}
5
6function Profile({ user }) {
7  // Only use default if null/undefined (not for empty string/0)
8  return <p>Age: {user.age ?? 'Unknown'}</p>
9}

Switch Case Pattern

For multiple conditions:

jsx
1function StatusBadge({ status }) {
2  switch (status) {
3    case 'pending':
4      return <span className="badge yellow">Pending</span>
5    case 'approved':
6      return <span className="badge green">Approved</span>
7    case 'rejected':
8      return <span className="badge red">Rejected</span>
9    default:
10      return <span className="badge gray">Unknown</span>
11  }
12}

Object Mapping

Cleaner alternative to switch:

jsx
1const STATUS_COMPONENTS = {
2  pending: <PendingIcon />,
3  approved: <ApprovedIcon />,
4  rejected: <RejectedIcon />,
5}
6
7function StatusIcon({ status }) {
8  return STATUS_COMPONENTS[status] || <DefaultIcon />
9}
10
11// With styles
12const STATUS_STYLES = {
13  pending: { color: 'yellow', text: 'Pending' },
14  approved: { color: 'green', text: 'Approved' },
15  rejected: { color: 'red', text: 'Rejected' },
16}
17
18function StatusBadge({ status }) {
19  const config = STATUS_STYLES[status] || { color: 'gray', text: 'Unknown' }
20  
21  return (
22    <span style={{ backgroundColor: config.color }}>
23      {config.text}
24    </span>
25  )
26}

Rendering Lists Conditionally

jsx
1function ProductList({ products, isLoading }) {
2  if (isLoading) {
3    return <LoadingSpinner />
4  }
5  
6  if (products.length === 0) {
7    return <EmptyState message="No products found" />
8  }
9  
10  return (
11    <ul>
12      {products.map(product => (
13        <ProductItem key={product.id} product={product} />
14      ))}
15    </ul>
16  )
17}

Conditional Props

jsx
1function Button({ isLoading, children, ...props }) {
2  return (
3    <button 
4      disabled={isLoading}
5      className={isLoading ? 'loading' : ''}
6      {...props}
7    >
8      {isLoading ? 'Loading...' : children}
9    </button>
10  )
11}
12
13// Conditional attributes
14function Link({ href, isExternal, children }) {
15  return (
16    <a 
17      href={href}
18      {...(isExternal && { target: '_blank', rel: 'noopener noreferrer' })}
19    >
20      {children}
21    </a>
22  )
23}

Conditional Classes

jsx
1function NavItem({ isActive, children }) {
2  // String concatenation
3  return (
4    <li className={`nav-item ${isActive ? 'active' : ''}`}>
5      {children}
6    </li>
7  )
8}
9
10// Using classnames library
11import clsx from 'clsx'
12
13function NavItem({ isActive, isDisabled, children }) {
14  return (
15    <li className={clsx('nav-item', {
16      'active': isActive,
17      'disabled': isDisabled,
18    })}>
19      {children}
20    </li>
21  )
22}

Conditional Wrapper

jsx
1function ConditionalWrapper({ condition, wrapper, children }) {
2  return condition ? wrapper(children) : children
3}
4
5// Usage
6function Link({ href, isExternal, children }) {
7  return (
8    <ConditionalWrapper
9      condition={isExternal}
10      wrapper={(children) => (
11        <a href={href} target="_blank" rel="noopener">
12          {children}
13        </a>
14      )}
15    >
16      {isExternal ? children : <a href={href}>{children}</a>}
17    </ConditionalWrapper>
18  )
19}

Rendering null

To render nothing:

jsx
1function WarningBanner({ warning }) {
2  if (!warning) {
3    return null  // Renders nothing
4  }
5  
6  return <div className="warning">{warning}</div>
7}

Complex Conditions

Extract complex logic into variables or functions:

jsx
1function Dashboard({ user, subscription, features }) {
2  const canAccessPremium = subscription.plan === 'premium' && subscription.isActive
3  const hasNewFeatures = features.some(f => f.isNew && f.enabled)
4  const showUpgradeBanner = !canAccessPremium && user.trialEnded
5  
6  return (
7    <div>
8      {showUpgradeBanner && <UpgradeBanner />}
9      
10      {canAccessPremium && <PremiumContent />}
11      
12      {hasNewFeatures && <NewFeaturesAnnouncement />}
13    </div>
14  )
15}

Conditional rendering is fundamental to building dynamic React UIs!