Learn how to show or hide content based on conditions, making your React components truly dynamic and responsive to user interactions.
Imagine you're building a login page. When a user is logged in, you want to show "Welcome back, John!" but when they're not logged in, you want to show "Please sign in." This is conditional rendering - showing different content based on certain conditions.
Think of it like a traffic light for your UI:
This is one of the most powerful features in React, and you'll use it in almost every component you build!
Let's start with a real-world problem. Imagine you're building a simple weather app:
function WeatherApp() {
const [weather, setWeather] = useState(null);
return (
<div>
<h1>Weather App</h1>
<p>Temperature: {weather.temperature}ยฐF</p> {/* This will crash! */}
<p>Condition: {weather.condition}</p>
</div>
);
}
What's wrong here? When the component first loads, "weather" is "null", so trying to access "weather.temperature" will cause an error!
We need a way to say: "Only show the weather information IF we actually have weather data."
Here's what happens without proper conditional rendering:
function UserProfile() {
const [user, setUser] = useState(null); // Starts as null
return (
<div>
<h1>User Profile</h1>
<p>Name: {user.name}</p> {/* Error! */}
<p>Email: {user.email}</p> {/* Error! */}
</div>
);
}
This component will immediately crash because "user" is "null" at first, and we can't access properties of "null".
The most basic way to handle this is with a simple if statement:
function UserProfile() {
const [user, setUser] = useState(null);
// If no user, show loading message
if (!user) {
return <p>Loading user profile...</p>;
}
// If we have a user, show their info
return (
<div>
<h1>User Profile</h1>
<p>Name: {user.name}</p>
<p>Email: {user.email}</p>
</div>
);
}
What's happening here?
Let's create a simple login system to understand conditional rendering step by step.
function LoginSystem() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [username, setUsername] = useState("");
return (
<div>
<h1>My App</h1>
{/* We'll add conditional content here */}
</div>
);
}
function LoginSystem() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [username, setUsername] = useState("");
if (isLoggedIn) {
return (
<div>
<h1>Welcome back, {username}!</h1>
<button onClick={() => setIsLoggedIn(false)}>
Logout
</button>
</div>
);
}
return (
<div>
<h1>Please Sign In</h1>
<input
type="text"
placeholder="Enter username"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<button onClick={() => setIsLoggedIn(true)}>
Login
</button>
</div>
);
}
Try it in your mind: What happens when you click the Login button? What about the Logout button?
This works great, but returning completely different JSX can make components hard to manage. Let's learn better approaches!
The ternary operator is like a compact if-else statement that works inside JSX. The syntax is:
condition ? "Show this if true" : "Show this if false"
Let's rewrite our login system using the ternary operator:
function LoginSystem() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [username, setUsername] = useState("");
return (
<div>
<h1>My App</h1>
{isLoggedIn ? (
// Show this if logged in
<div>
<p>Welcome back, {username}!</p>
<button onClick={() => setIsLoggedIn(false)}>
Logout
</button>
</div>
) : (
// Show this if not logged in
<div>
<input
type="text"
placeholder="Enter username"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<button onClick={() => setIsLoggedIn(true)}>
Login
</button>
</div>
)}
</div>
);
}
Benefits of this approach:
Sometimes you don't need an "else" case - you just want to show something or show nothing. That's where the logical AND ("&&") operator shines:
function NotificationBell() {
const [hasNewMessages, setHasNewMessages] = useState(true);
const [messageCount, setMessageCount] = useState(3);
return (
<div>
<h2>๐ Notifications</h2>
{hasNewMessages && (
<div className="notification-badge">
You have {messageCount} new messages!
</div>
)}
<button onClick={() => setHasNewMessages(!hasNewMessages)}>
Toggle Notifications
</button>
</div>
);
}
How it works:
Let's build a shopping cart component that demonstrates multiple conditional rendering patterns:
function ShoppingCart() {
const [items, setItems] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [showCart, setShowCart] = useState(false);
const addItem = (itemName) => {
setItems([...items, { id: Date.now(), name: itemName, price: 19.99 }]);
};
const removeItem = (itemId) => {
setItems(items.filter(item => item.id !== itemId));
};
const getTotalPrice = () => {
return items.reduce((total, item) => total + item.price, 0).toFixed(2);
};
return (
<div style={{ padding: "20px" }}>
<h1>๐ Shopping Cart Demo</h1>
{/* Show/Hide Cart Button */}
<button onClick={() => setShowCart(!showCart)}>
{showCart ? "Hide Cart" : "Show Cart"} ({items.length} items)
</button>
{/* Quick Add Buttons */}
<div style={{ margin: "10px 0" }}>
<button onClick={() => addItem("Cool T-Shirt")}>Add T-Shirt</button>
<button onClick={() => addItem("Nice Shoes")}>Add Shoes</button>
<button onClick={() => addItem("Awesome Hat")}>Add Hat</button>
</div>
{/* Cart Content - Only show if showCart is true */}
{showCart && (
<div style={{
border: "2px solid #ccc",
padding: "15px",
marginTop: "10px",
borderRadius: "5px"
}}>
<h2>Your Cart</h2>
{/* Loading State */}
{isLoading ? (
<p>Loading cart...</p>
) : (
<div>
{/* Empty Cart vs Items */}
{items.length === 0 ? (
<p>Your cart is empty. Add some items!</p>
) : (
<div>
<ul>
{items.map(item => (
<li key={item.id} style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
padding: "5px 0"
}}>
<span>{item.name} - $" + item.price + "</span>
<button onClick={() => removeItem(item.id)}>
Remove
</button>
</li>
))}
</ul>
<div style={{
marginTop: "15px",
fontSize: "18px",
fontWeight: "bold"
}}>
Total: $" + getTotalPrice() + "
</div>
{/* Show checkout button only if cart has items */}
{items.length > 0 && (
<button style={{
backgroundColor: "#4CAF50",
color: "white",
padding: "10px 20px",
marginTop: "10px",
border: "none",
borderRadius: "5px"
}}>
Checkout
</button>
)}
</div>
)}
</div>
)}
</div>
)}
</div>
);
}
What conditional rendering patterns do you see?
Sometimes you have complex logic that's hard to read in JSX. Extract it to a function:
function UserStatus({ user }) {
const renderUserStatus = () => {
if (!user) {
return <p>No user found</p>;
}
if (user.isOnline && user.isActive) {
return <span style={{color: "green"}}>๐ข Online & Active</span>;
}
if (user.isOnline) {
return <span style={{color: "orange"}}>๐ก Online but Away</span>;
}
if (user.lastSeen) {
return <span style={{color: "gray"}}>๐ด Last seen: {user.lastSeen}</span>;
}
return <span style={{color: "red"}}>๐ด Offline</span>;
};
return (
<div>
<h3>User Status</h3>
{renderUserStatus()}
</div>
);
}
You can conditionally apply CSS classes and styles:
function Message({ type, children }) {
return (
<div
className={type === "error" ? "error-message" : "info-message"}
style={{
padding: "10px",
borderRadius: "5px",
backgroundColor: type === "error" ? "#ffebee" : "#e3f2fd",
color: type === "error" ? "#c62828" : "#1565c0",
border: type === "error" ? "1px solid #e57373" : "1px solid #64b5f6"
}}
>
{type === "error" ? "โ" : "โน๏ธ"} {children}
</div>
);
}
// Usage:
<Message type="error">Something went wrong!</Message>
<Message type="info">Here's some helpful information.</Message>
Let's put your conditional rendering skills to the test! Try building this weather widget:
Requirements:
Here's the starter code:
function WeatherWidget() {
const [weather, setWeather] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const getWeather = () => {
setIsLoading(true);
setError(null);
// Simulate API call
setTimeout(() => {
// Randomly succeed or fail for demo
if (Math.random() > 0.3) {
setWeather({
temperature: Math.round(Math.random() * 30 + 50), // 50-80ยฐF
condition: ["sunny", "cloudy", "rainy"][Math.floor(Math.random() * 3)],
city: "New York"
});
} else {
setError("Failed to fetch weather data");
}
setIsLoading(false);
}, 2000);
};
// Your code here! Use conditional rendering to:
// 1. Show loading spinner when isLoading is true
// 2. Show error message if error exists
// 3. Show weather data if weather exists
// 4. Show "Get Weather" button if no weather data
return (
<div style={{ padding: "20px", textAlign: "center" }}>
<h2>๐ค๏ธ Weather Widget</h2>
{/* Add your conditional rendering here */}
</div>
);
}
Solution (try it yourself first!):
function WeatherWidget() {
const [weather, setWeather] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const getWeather = () => {
setIsLoading(true);
setError(null);
setTimeout(() => {
if (Math.random() > 0.3) {
setWeather({
temperature: Math.round(Math.random() * 30 + 50),
condition: ["sunny", "cloudy", "rainy"][Math.floor(Math.random() * 3)],
city: "New York"
});
} else {
setError("Failed to fetch weather data");
}
setIsLoading(false);
}, 2000);
};
const getWeatherIcon = (condition) => {
switch(condition) {
case "sunny": return "โ๏ธ";
case "cloudy": return "โ๏ธ";
case "rainy": return "๐ง๏ธ";
default: return "๐ค๏ธ";
}
};
const reset = () => {
setWeather(null);
setError(null);
};
return (
<div style={{ padding: "20px", textAlign: "center" }}>
<h2>๐ค๏ธ Weather Widget</h2>
{isLoading ? (
// Loading state
<div>
<p>๐ Getting weather data...</p>
<div>Loading...</div>
</div>
) : error ? (
// Error state
<div>
<p style={{ color: "red" }}>โ {error}</p>
<button onClick={getWeather}>Try Again</button>
</div>
) : weather ? (
// Weather data loaded
<div style={{
backgroundColor: "#f0f8ff",
padding: "20px",
borderRadius: "10px",
margin: "10px 0"
}}>
<h3>{weather.city}</h3>
<div style={{ fontSize: "48px" }}>
{getWeatherIcon(weather.condition)}
</div>
<p style={{ fontSize: "24px", margin: "10px 0" }}>
{weather.temperature}ยฐF
</p>
<p style={{ textTransform: "capitalize" }}>
{weather.condition}
</p>
<button onClick={reset} style={{ marginTop: "10px" }}>
Get New Weather
</button>
</div>
) : (
// Initial state - no weather data
<div>
<p>Click to get current weather information</p>
<button onClick={getWeather}>Get Weather</button>
</div>
)}
</div>
);
}
// โ This will show "0" when count is 0
{count && <p>Count: {count}</p>}
// โ
Be explicit with your conditions
{count > 0 && <p>Count: {count}</p>}
{count !== 0 && <p>Count: {count}</p>}
// โ Users see nothing while data loads
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
return (
<div>
{user && (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
)}
</div>
);
}
// โ
Always handle loading states
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [isLoading, setIsLoading] = useState(true);
return (
<div>
{isLoading ? (
<p>Loading user profile...</p>
) : user ? (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
) : (
<p>User not found</p>
)}
</div>
);
}
// โ Hard to read and maintain
{user && user.profile && user.profile.settings && user.profile.settings.notifications && user.profile.settings.notifications.email ? (
<EmailNotificationSettings />
) : (
<DefaultSettings />
)}
// โ
Extract complex logic to variables or functions
const canShowEmailSettings = user?.profile?.settings?.notifications?.email;
return (
<div>
{canShowEmailSettings ? (
<EmailNotificationSettings />
) : (
<DefaultSettings />
)}
</div>
);
Congratulations! You now know how to:
โ
Show different content based on conditions
โ
Handle loading, error, and success states
โ
Use ternary operators for either/or scenarios
โ
Use logical AND for show/hide scenarios
โ
Build complex conditional UIs
โ
Avoid common conditional rendering pitfalls
Test your understanding:
Answers: 1) && shows something or nothing, ?: shows one thing or another, 2) Shows "0" when count is 0, 3) When logic is complex or repeated, 4) Use nested ternary operators or if-else chains
In our next lesson, we'll learn about Lists and Keys - how to render multiple items dynamically and why React needs special "key" props. You'll discover how to build dynamic lists, handle user interactions with list items, and optimize list performance.
Combined with conditional rendering, lists will let you build truly dynamic and interactive user interfaces!