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!
