React Conditional Rendering - Complete Guide
Conditional rendering is a fundamental concept in React that lets you create components that can render different things based on different conditions. This guide covers all the ways to conditionally render content in React applications.
What is Conditional Rendering?
Conditional rendering means rendering different JSX elements or components based on certain conditions. Just like JavaScript uses if/else statements or switch statements to control program flow, React uses conditional rendering to control what gets displayed on the screen.
Basic Conditional Rendering Methods
1. Using the if Statement
The simplest way to conditionally render content is using a regular JavaScript if statement:
function UserGreeting({ isLoggedIn }) {
if (isLoggedIn) {
return <h1>Welcome back!</h1>;
}
return <h1>Please sign in.</h1>;
}
function App() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
return (
<div>
<UserGreeting isLoggedIn={isLoggedIn} />
<button onClick={() => setIsLoggedIn(!isLoggedIn)}>
{isLoggedIn ? 'Sign Out' : 'Sign In'}
</button>
</div>
);
}2. Using the Ternary Operator
For inline conditional rendering, the ternary operator is perfect:
function Message({ showMessage }) {
return (
<div>
{showMessage ? (
<p>This message is visible!</p>
) : (
<p>This message is hidden.</p>
)}
</div>
);
}
function App() {
const [showMessage, setShowMessage] = useState(true);
return (
<div>
<Message showMessage={showMessage} />
<button onClick={() => setShowMessage(!showMessage)}>
Toggle Message
</button>
</div>
);
}3. Using Logical AND (&&) Operator
The logical AND operator is great for rendering something only when a condition is true:
function Notification({ hasNewMessages, messageCount }) {
return (
<div>
<h2>Notifications</h2>
{hasNewMessages && (
<p>You have {messageCount} new messages!</p>
)}
</div>
);
}
function App() {
const [hasNewMessages, setHasNewMessages] = useState(true);
const messageCount = 5;
return (
<div>
<Notification
hasNewMessages={hasNewMessages}
messageCount={messageCount}
/>
<button onClick={() => setHasNewMessages(!hasNewMessages)}>
Toggle Notifications
</button>
</div>
);
}Advanced Conditional Rendering Patterns
1. Element Variables
You can store JSX elements in variables and use them in your render method:
function LoginControl() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
let button;
if (isLoggedIn) {
button = <LogoutButton onClick={() => setIsLoggedIn(false)} />;
} else {
button = <LoginButton onClick={() => setIsLoggedIn(true)} />;
}
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
{button}
</div>
);
}
function Greeting({ isLoggedIn }) {
if (isLoggedIn) {
return <h1>Welcome back!</h1>;
}
return <h1>Please sign in.</h1>;
}
function LoginButton({ onClick }) {
return <button onClick={onClick}>Login</button>;
}
function LogoutButton({ onClick }) {
return <button onClick={onClick}>Logout</button>;
}2. Immediately Invoked Function Expressions (IIFE)
For more complex conditions, you can use IIFEs:
function UserStatus({ user, isLoading }) {
return (
<div>
{(() => {
if (isLoading) {
return <div>Loading user data...</div>;
}
if (!user) {
return <div>No user data available</div>;
}
if (user.isActive) {
return <div className="status-active">User is active</div>;
} else {
return <div className="status-inactive">User is inactive</div>;
}
})()}
</div>
);
}3. Switch Statement Pattern
For multiple conditions, a switch statement can be cleaner:
function StatusMessage({ status }) {
const getStatusMessage = () => {
switch (status) {
case 'loading':
return <div>Loading...</div>;
case 'success':
return <div className="success">Operation completed successfully!</div>;
case 'error':
return <div className="error">An error occurred. Please try again.</div>;
case 'warning':
return <div className="warning">Please review your input.</div>;
default:
return <div>Unknown status</div>;
}
};
return (
<div>
<h2>Status</h2>
{getStatusMessage()}
</div>
);
}Conditional Rendering with Components
1. Component Wrappers
Create wrapper components that handle conditional logic:
function ConditionalWrapper({ condition, wrapper, children }) {
return condition ? wrapper(children) : children;
}
function App() {
const [isLink, setIsLink] = useState(false);
return (
<div>
<ConditionalWrapper
condition={isLink}
wrapper={children => <a href="#">{children}</a>}
>
<p>This content might be wrapped in a link</p>
</ConditionalWrapper>
<button onClick={() => setIsLink(!isLink)}>
Toggle Link Wrapper
</button>
</div>
);
}2. Higher-Order Components
Use HOCs for conditional rendering logic:
function withAuth(WrappedComponent) {
return function AuthenticatedComponent(props) {
const [isAuthenticated, setIsAuthenticated] = useState(false);
if (!isAuthenticated) {
return <div>Please log in to access this content.</div>;
}
return <WrappedComponent {...props} />;
};
}
function SecretContent() {
return <div>This is secret content only for authenticated users!</div>;
}
const ProtectedSecretContent = withAuth(SecretContent);Common Use Cases
1. Loading States
Handle different loading and error states:
function DataLoader({ url }) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
const response = await fetch(url);
const result = await response.json();
setData(result);
setError(null);
} catch (err) {
setError(err.message);
setData(null);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]);
if (loading) {
return <div className="loading">Loading data...</div>;
}
if (error) {
return <div className="error">Error: {error}</div>;
}
if (!data || data.length === 0) {
return <div className="no-data">No data available</div>;
}
return (
<div className="data-container">
{data.map(item => (
<div key={item.id} className="data-item">
{item.name}
</div>
))}
</div>
);
}2. Form Validation
Show different UI based on form validation state:
function FormValidation() {
const [formData, setFormData] = useState({
email: '',
password: ''
});
const [errors, setErrors] = useState({});
const [isSubmitted, setIsSubmitted] = useState(false);
const validateForm = () => {
const newErrors = {};
if (!formData.email) {
newErrors.email = 'Email is required';
} else if (!/\S+@\S+\.\S+/.test(formData.email)) {
newErrors.email = 'Email is invalid';
}
if (!formData.password) {
newErrors.password = 'Password is required';
} else if (formData.password.length < 6) {
newErrors.password = 'Password must be at least 6 characters';
}
return newErrors;
};
const handleSubmit = (e) => {
e.preventDefault();
const newErrors = validateForm();
if (Object.keys(newErrors).length === 0) {
setIsSubmitted(true);
setErrors({});
} else {
setErrors(newErrors);
}
};
const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({ ...prev, [name]: value }));
};
if (isSubmitted) {
return (
<div className="success-message">
<h2>Form submitted successfully!</h2>
<p>Email: {formData.email}</p>
</div>
);
}
return (
<form onSubmit={handleSubmit}>
<div className="form-group">
<label>Email:</label>
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
className={errors.email ? 'error' : ''}
/>
{errors.email && <span className="error-message">{errors.email}</span>}
</div>
<div className="form-group">
<label>Password:</label>
<input
type="password"
name="password"
value={formData.password}
onChange={handleChange}
className={errors.password ? 'error' : ''}
/>
{errors.password && <span className="error-message">{errors.password}</span>}
</div>
<button type="submit">Submit</button>
</form>
);
}3. Feature Flags
Control features based on configuration:
function FeatureFlaggedComponent({ featureFlags }) {
return (
<div>
<h1>Application</h1>
{/* Basic feature - always available */}
<div className="basic-feature">
<h2>Basic Feature</h2>
<p>This feature is available to all users.</p>
</div>
{/* Premium feature - only for premium users */}
{featureFlags.isPremium && (
<div className="premium-feature">
<h2>Premium Feature</h2>
<p>This feature is only available to premium users.</p>
</div>
)}
{/* Beta feature - only for beta testers */}
{featureFlags.isBetaTester && (
<div className="beta-feature">
<h2>Beta Feature</h2>
<p>This feature is currently in beta.</p>
</div>
)}
{/* Admin features - only for admins */}
{featureFlags.isAdmin && (
<div className="admin-feature">
<h2>Admin Panel</h2>
<p>Administrative controls and settings.</p>
</div>
)}
</div>
);
}Performance Considerations
1. Avoiding Unnecessary Renders
Use React.memo to prevent unnecessary re-renders:
const ExpensiveComponent = React.memo(({ data, isVisible }) => {
console.log('ExpensiveComponent rendered');
return (
<div>
{isVisible && (
<div>
{data.map(item => (
<div key={item.id}>
{item.name} - {item.value}
</div>
))}
</div>
)}
</div>
);
});2. Lazy Loading Components
Load components only when needed:
import React, { lazy, Suspense } from 'react';
const LazyComponent = lazy(() => import('./LazyComponent'));
function App() {
const [showComponent, setShowComponent] = useState(false);
return (
<div>
<button onClick={() => setShowComponent(true)}>
Load Component
</button>
{showComponent && (
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
)}
</div>
);
}Best Practices
1. Keep Conditional Logic Simple
// Bad - complex inline logic
return (
<div>
{user && user.isActive && user.permissions.includes('admin') && !user.isBanned ? (
<AdminPanel />
) : (
<UserPanel />
)}
</div>
);
// Good - extract to a variable or function
function App({ user }) {
const canAccessAdmin = user &&
user.isActive &&
user.permissions.includes('admin') &&
!user.isBanned;
return (
<div>
{canAccessAdmin ? <AdminPanel /> : <UserPanel />}
</div>
);
}2. Use Descriptive Variable Names
// Bad
return (
<div>
{a && b ? <Component1 /> : <Component2 />}
</div>
);
// Good
return (
<div>
{isLoggedIn && hasPermission ? <AdminDashboard /> : <UserDashboard />}
</div>
);3. Handle Edge Cases
function UserList({ users }) {
// Handle null/undefined
if (!users) {
return <div>No user data available</div>;
}
// Handle empty array
if (users.length === 0) {
return <div>No users found</div>;
}
// Render users
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}Summary
Conditional rendering is essential for building dynamic React applications. Remember these key points:
- Use
ifstatements for complex conditions outside JSX - Use ternary operators for simple either/or conditions
- Use logical AND (
&&) for showing/hiding elements - Extract complex logic into variables or functions
- Handle loading, error, and empty states
- Consider performance when conditionally rendering expensive components
- Keep conditional logic readable and maintainable
Mastering conditional rendering will help you create more flexible and user-friendly React applications that can adapt to different states and conditions.
External Resources:
- React Official Docs - Conditional Rendering
- React Patterns - Conditional Rendering
- MDN Web Docs - Conditional (Ternary) Operator
Related Tutorials: