Back to Tutorial
State and Events
Hands-on Practice
25 min

Conditional Rendering

Learn how to show or hide content based on conditions, making your React components truly dynamic and responsive to user interactions.

Learning Objectives

  • Understand why conditional rendering is essential
  • Learn different ways to conditionally show content
  • Master the ternary operator for JSX
  • Use logical AND operator effectively
  • Handle complex conditional scenarios
  • Build dynamic user interfaces with conditions

Conditional Rendering

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:

  • 🔴 If user is not logged in → Show login form
  • 🟢 If user is logged in → Show welcome message
  • 🟡 If user is loading → Show loading spinner

This is one of the most powerful features in React, and you'll use it in almost every component you build!

Why Do We Need Conditional Rendering?

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."

The Problem Without Conditional Rendering

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".

Solution 1: The Simple If Statement

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?

  1. We check if "user" is null or undefined
  2. If it is, we return early with a loading message
  3. If we have user data, we show the profile
  4. This prevents any crashes!

Let's Build Something Together

Let's create a simple login system to understand conditional rendering step by step.

Step 1: Set Up the Basic Component

function LoginSystem() { const [isLoggedIn, setIsLoggedIn] = useState(false); const [username, setUsername] = useState(""); return ( <div> <h1>My App</h1> {/* We'll add conditional content here */} </div> ); }

Step 2: Add Basic Conditional Logic

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: Your New Best Friend

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:

  • Everything stays in one return statement
  • Easier to see the overall component structure
  • More flexible for complex layouts

The Logical AND Operator: Show or Nothing

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:

  • If "hasNewMessages" is "true", React shows the notification
  • If "hasNewMessages" is "false", React shows nothing
  • It's perfect for "show or hide" scenarios

Real-World Example: Shopping Cart

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?

  1. Ternary operator for button text: "showCart ? 'Hide Cart' : 'Show Cart'"
  2. Logical AND for showing cart: "showCart && <div>...</div>"
  3. Ternary operator for loading vs content: "isLoading ? <Loading/> : <Content/>"
  4. Ternary operator for empty vs filled cart: "items.length === 0 ? <Empty/> : <Items/>"
  5. Logical AND for checkout button: "items.length > 0 && <button>Checkout</button>"

Advanced Conditional Patterns

Pattern 1: Multiple Conditions with Functions

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> ); }

Pattern 2: Conditional Classes and Styles

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>

Practice Exercise: Build a Weather Widget

Let's put your conditional rendering skills to the test! Try building this weather widget:

Requirements:

  1. Start with no weather data (null)
  2. Have a "Get Weather" button
  3. Show loading state when fetching
  4. Show weather info when loaded
  5. Show different icons based on weather condition
  6. Handle error states

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> ); }

Common Conditional Rendering Mistakes

Mistake 1: Forgetting About Falsy Values

// ❌ 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>}

Mistake 2: Not Handling Loading States

// ❌ 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> ); }

Mistake 3: Overly Complex Conditions in JSX

// ❌ 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> );

When to Use Each Pattern

Use if statements when:

  • You need to return completely different components
  • You have complex logic that would make JSX hard to read
  • You're doing early returns for error/loading states

Use ternary operators when:

  • You have a clear "this or that" choice
  • Both options are relatively simple
  • You want to keep everything in the JSX return

Use logical AND when:

  • You want to show something or nothing
  • The condition is simple
  • You're toggling visibility of elements

What We've Learned

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

Quick Recap Quiz

Test your understanding:

  1. What's the difference between "&&" and "? :" for conditional rendering?
  2. Why might "{count && <p>Count: {count}</p>}" cause problems?
  3. When should you extract conditional logic to a separate function?
  4. How do you handle multiple possible states (loading, error, success)?

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

What's Next?

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!