Understanding Conditional Rendering
Conditional rendering in React allows you to render different UI elements based on certain conditions. Just like JavaScript's conditional statements, you can use if statements, ternary operators, and logical operators to control what gets rendered.
Core Concept: In React, you can create different components or elements and render only the ones you need based on the application state. This makes your UI dynamic and responsive to user interactions and data changes.
Using If/Else Statements
The most straightforward way to conditionally render components is using regular JavaScript if/else statements outside the return statement:
function Greeting({ isLoggedIn }) {
if (isLoggedIn) {
return <h1>Welcome back!</h1>;
} else {
return <h1>Please sign in.</h1>;
}
}
// Usage
function App() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
<button onClick={() => setIsLoggedIn(!isLoggedIn)}>
{isLoggedIn ? 'Logout' : 'Login'}
</button>
</div>
);
}
Ternary Operator
The ternary operator (condition ? true : false) is perfect for inline conditional rendering within JSX:
function UserStatus({ isActive }) {
return (
<div>
<h2>User Status</h2>
<p>
You are currently{" "}
{isActive ? (
<span style={{ color: 'green' }}>online</span>
) : (
<span style={{ color: 'red' }}>offline</span>
)}
</p>
<div className={isActive ? 'status-active' : 'status-inactive'}>
{isActive ? 'Connected' : 'Disconnected'}
</div>
</div>
);
}
Best Practice: Use ternary operators for simple conditions with both true and false cases. For more complex conditions or when you only need to render something in one case, use the logical && operator instead.
Logical AND (&&) Operator
The && operator is ideal when you want to render something only if a condition is true, and render nothing otherwise:
function Notifications({ messages }) {
return (
<div>
<h2>Inbox</h2>
{messages.length > 0 && (
<div className="notification-badge">
You have {messages.length} unread messages
</div>
)}
{messages.length === 0 && (
<p>No new messages</p>
)}
</div>
);
}
// Usage
function App() {
const [messages, setMessages] = useState(['Hello', 'Hi there']);
return (
<div>
<Notifications messages={messages} />
<button onClick={() => setMessages([])}>
Clear Messages
</button>
</div>
);
}
Common Pitfall: Be careful with falsy values on the left side of &&. For example, {count && <div>Count: {count}</div>} will render "0" when count is 0. Instead, use {count > 0 && ...} or {!!count && ...}.
Element Variables
You can store elements in variables and conditionally include them in your render output:
function LoginControl() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
let button;
if (isLoggedIn) {
button = <button onClick={() => setIsLoggedIn(false)}>Logout</button>;
} else {
button = <button onClick={() => setIsLoggedIn(true)}>Login</button>;
}
let message;
if (isLoggedIn) {
message = <p>You have access to premium features.</p>;
} else {
message = <p>Please log in to access premium features.</p>;
}
return (
<div>
<h1>Account Status</h1>
{message}
{button}
</div>
);
}
Multiple Conditions with Switch
For multiple conditions, you can use switch statements or create a mapping object:
function StatusMessage({ status }) {
// Method 1: Switch statement
const getMessageWithSwitch = () => {
switch(status) {
case 'loading':
return <div className="spinner">Loading...</div>;
case 'success':
return <div className="success">Data loaded successfully!</div>;
case 'error':
return <div className="error">Failed to load data.</div>;
default:
return <div>No status</div>;
}
};
// Method 2: Object mapping (cleaner)
const statusComponents = {
loading: <div className="spinner">Loading...</div>,
success: <div className="success">Data loaded successfully!</div>,
error: <div className="error">Failed to load data.</div>,
idle: <div>No status</div>
};
return (
<div>
{/* Using switch */}
{getMessageWithSwitch()}
{/* Or using object mapping */}
{statusComponents[status] || statusComponents.idle}
</div>
);
}
Rendering Nothing (Null)
Sometimes you want a component to hide itself. Return null to render nothing:
function WarningBanner({ warning, onClose }) {
if (!warning) {
return null; // Don't render anything
}
return (
<div className="warning-banner">
<span>{warning}</span>
<button onClick={onClose}>×</button>
</div>
);
}
// Usage
function App() {
const [warning, setWarning] = useState('Your session will expire in 5 minutes');
return (
<div>
<WarningBanner
warning={warning}
onClose={() => setWarning(null)}
/>
<h1>Dashboard</h1>
</div>
);
}
Important: Returning null from a component does not affect the lifecycle methods. The component still mounts and updates normally, it just doesn't render anything visible.
Conditional CSS Classes
You can conditionally apply CSS classes to change the appearance of elements:
function Button({ isPrimary, isDisabled, children }) {
// Method 1: Template literals
const className1 = `btn ${isPrimary ? 'btn-primary' : 'btn-secondary'} ${isDisabled ? 'btn-disabled' : ''}`;
// Method 2: Array join
const className2 = [
'btn',
isPrimary ? 'btn-primary' : 'btn-secondary',
isDisabled && 'btn-disabled'
].filter(Boolean).join(' ');
// Method 3: Object-based (requires classnames library)
// const className3 = classnames({
// 'btn': true,
// 'btn-primary': isPrimary,
// 'btn-secondary': !isPrimary,
// 'btn-disabled': isDisabled
// });
return (
<button className={className1} disabled={isDisabled}>
{children}
</button>
);
}
// Usage
function App() {
return (
<div>
<Button isPrimary={true} isDisabled={false}>
Save
</Button>
<Button isPrimary={false} isDisabled={false}>
Cancel
</Button>
<Button isPrimary={true} isDisabled={true}>
Loading...
</Button>
</div>
);
}
Complex Conditional Rendering Example
Here's a real-world example combining multiple conditional rendering techniques:
function UserDashboard({ user, isLoading, error }) {
// Early returns for special cases
if (isLoading) {
return <div className="loading">Loading user data...</div>;
}
if (error) {
return (
<div className="error-container">
<h2>Error</h2>
<p>{error.message}</p>
<button onClick={() => window.location.reload()}>
Retry
</button>
</div>
);
}
if (!user) {
return <div>No user found</div>;
}
// Main render with nested conditions
return (
<div className="dashboard">
<h1>Welcome, {user.name}!</h1>
{/* Conditional badge */}
{user.isPremium && (
<span className="premium-badge">Premium Member</span>
)}
{/* Conditional content based on account type */}
{user.accountType === 'admin' ? (
<AdminPanel />
) : user.accountType === 'moderator' ? (
<ModeratorPanel />
) : (
<UserPanel />
)}
{/* Conditional notification */}
{user.notifications.length > 0 && (
<div className="notifications">
<h3>You have {user.notifications.length} notifications</h3>
{user.notifications.map(notif => (
<div key={notif.id}>{notif.message}</div>
))}
</div>
)}
{/* Conditional warning */}
{!user.emailVerified && (
<div className="warning">
Please verify your email address
</div>
)}
</div>
);
}
Performance Tip: Use early returns for special cases (loading, error, no data) at the top of your component. This keeps your main render logic clean and easier to understand.
Exercise 1: Todo List with Filters
Create a todo list with conditional rendering for different views:
- Display "All", "Active", and "Completed" filter buttons
- Show different lists based on the selected filter
- If no todos match the filter, show "No todos found" message
- Show a counter: "X active todos" (only if there are active todos)
- Show a "Clear completed" button (only if there are completed todos)
- Show different styles for completed vs active todos
Hint: Use state to track the current filter and use conditional rendering to show the appropriate todos.
Exercise 2: Authentication Flow
Build a component that renders different screens based on authentication state:
- Show a loading spinner while checking authentication
- Show login form if not authenticated
- Show user dashboard if authenticated
- Show error message if authentication failed
- In the dashboard, show admin tools only if user is an admin
- Show a welcome banner only on first login (track with state)
Bonus: Add a "remember me" checkbox that conditionally displays in the login form.
Exercise 3: Product Card with Conditional Features
Create a product card component with various conditional elements:
- Show "Sale" badge if product has a discount
- Show original price (crossed out) and sale price if on sale
- Show "Out of Stock" message if quantity is 0
- Disable "Add to Cart" button if out of stock
- Show star rating only if product has reviews
- Show "New" badge if product was added within last 7 days
- Apply different CSS classes based on product category
Hint: Pass a product object with properties like price, salePrice, quantity, rating, createdAt, and category.