What is Conditional Rendering?
Conditional rendering in React lets you show different content based on certain conditions. Instead of having all content visible at once, you can dynamically choose what to display based on state, props, or any other data.
// HTML
<div id="message"></div>
<button id="toggleBtn">Toggle</button>
// JavaScript
let isVisible = false;
const messageDiv = document.getElementById('message');
const toggleBtn = document.getElementById('toggleBtn');
function updateDisplay() {
if (isVisible) {
messageDiv.innerHTML = '<p>Hello! I am visible.</p>';
messageDiv.style.display = 'block';
} else {
messageDiv.style.display = 'none';
}
}
toggleBtn.addEventListener('click', () => {
isVisible = !isVisible;
updateDisplay();
});
updateDisplay(); // Initial render
function ConditionalExample() {
const [isVisible, setIsVisible] = useState(false);
return (
<div>
{/* Conditional rendering using && */}
{isVisible && <p>Hello! I am visible.</p>}
{/* Toggle button */}
<button onClick={() => setIsVisible(!isVisible)}>
Toggle
</button>
</div>
);
}
Conditional Rendering Techniques
1. Logical AND (&&) Operator
The most common way to conditionally render elements. Shows content only when condition is true.
function UserGreeting({ user }) {
return (
<div>
{/* Show greeting only if user exists */}
{user && <h2>Welcome back, {user.name}!</h2>}
{/* Show notifications only if user has any */}
{user?.notifications?.length > 0 && (
<div className="notifications">
You have {user.notifications.length} new messages
</div>
)}
{/* Show premium features only for premium users */}
{user?.isPremium && (
<div className="premium-features">
<h3>Premium Features</h3>
<p>Unlock advanced functionality</p>
</div>
)}
</div>
);
}
2. Ternary Operator (? :)
Perfect for showing one thing or another based on a condition.
function LoginStatus({ isLoggedIn, user }) {
return (
<div>
{/* Simple ternary for different content */}
<h1>
{isLoggedIn ? `Welcome, ${user.name}!` : 'Please log in'}
</h1>
{/* Ternary for different components */}
{isLoggedIn ? (
<UserDashboard user={user} />
) : (
<LoginForm />
)}
{/* Ternary for different styles */}
<button
className={isLoggedIn ? 'logout-btn' : 'login-btn'}
onClick={isLoggedIn ? handleLogout : handleLogin}
>
{isLoggedIn ? 'Logout' : 'Login'}
</button>
</div>
);
}
3. If/Else Statements
For complex logic, use regular JavaScript if/else statements before the return.
function UserProfile({ user, loading, error }) {
// Handle different states with if/else
if (loading) {
return (
<div className="loading">
<p>Loading user profile...</p>
<div className="spinner"></div>
</div>
);
}
if (error) {
return (
<div className="error">
<h2>Something went wrong</h2>
<p>{error.message}</p>
<button onClick={handleRetry}>Try Again</button>
</div>
);
}
if (!user) {
return (
<div className="no-user">
<h2>User not found</h2>
<p>The user you're looking for doesn't exist.</p>
</div>
);
}
// Main content when everything is good
return (
<div className="user-profile">
<img src={user.avatar} alt={`${user.name}'s avatar`} />
<h1>{user.name}</h1>
<p>{user.bio}</p>
</div>
);
}
4. Switch Statements
Useful when you have multiple conditions to check against a single value.
function StatusIndicator({ status }) {
const getStatusContent = () => {
switch (status) {
case 'loading':
return (
<div className="status loading">
<span className="icon">âŗ</span>
<span>Loading...</span>
</div>
);
case 'success':
return (
<div className="status success">
<span className="icon">â
</span>
<span>Success!</span>
</div>
);
case 'error':
return (
<div className="status error">
<span className="icon">â</span>
<span>Error occurred</span>
</div>
);
case 'warning':
return (
<div className="status warning">
<span className="icon">â ī¸</span>
<span>Warning</span>
</div>
);
default:
return (
<div className="status neutral">
<span className="icon">âšī¸</span>
<span>Ready</span>
</div>
);
}
};
return <div>{getStatusContent()}</div>;
}
Rendering Lists in React
One of the most common tasks in React is rendering lists of data. React makes this easy with JavaScript's map() function and JSX.
// HTML
<ul id="todo-list"></ul>
// JavaScript
const todos = [
{ id: 1, text: 'Learn JavaScript', done: true },
{ id: 2, text: 'Learn React', done: false },
{ id: 3, text: 'Build a project', done: false }
];
function renderTodos() {
const todoList = document.getElementById('todo-list');
todoList.innerHTML = ''; // Clear existing content
todos.forEach(todo => {
const li = document.createElement('li');
li.innerHTML = `
<span class="${todo.done ? 'done' : ''}">
${todo.text}
</span>
<button onclick="toggleTodo(${todo.id})">
${todo.done ? 'Undo' : 'Complete'}
</button>
`;
todoList.appendChild(li);
});
}
renderTodos(); // Initial render
function TodoList() {
const [todos, setTodos] = useState([
{ id: 1, text: 'Learn JavaScript', done: true },
{ id: 2, text: 'Learn React', done: false },
{ id: 3, text: 'Build a project', done: false }
]);
const toggleTodo = (id) => {
setTodos(prev =>
prev.map(todo =>
todo.id === id
? { ...todo, done: !todo.done }
: todo
)
);
};
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>
<span className={todo.done ? 'done' : ''}>
{todo.text}
</span>
<button onClick={() => toggleTodo(todo.id)}>
{todo.done ? 'Undo' : 'Complete'}
</button>
</li>
))}
</ul>
);
}
The Importance of Keys
When rendering lists, React needs a way to track each item uniquely.
The key prop helps React identify which items have changed,
been added, or removed, leading to better performance and fewer bugs.
đ Why Keys Matter
- Performance: React can efficiently update only changed items
- State preservation: Component state stays with the correct item
- Animation: Smooth transitions when items are added/removed
- Form inputs: Input values stay with the correct items
function ProductList({ products }) {
return (
<div className="product-grid">
{products.map(product => (
{/* â
Good: Using unique, stable ID */}
<ProductCard
key={product.id}
product={product}
/>
))}
</div>
);
}
function BadExample({ items }) {
return (
<ul>
{items.map((item, index) => (
{/* â Bad: Using array index as key */}
<li key={index}>{item.name}</li>
))}
</ul>
);
}
function GoodExample({ items }) {
return (
<ul>
{items.map(item => (
{/* â
Good: Using unique property */}
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}
function ComplexExample({ comments }) {
return (
<div>
{comments.map(comment => (
<div key={comment.id} className="comment">
<h4>{comment.author}</h4>
<p>{comment.text}</p>
{/* Nested list also needs keys */}
{comment.replies && (
<div className="replies">
{comment.replies.map(reply => (
<div key={reply.id} className="reply">
<strong>{reply.author}:</strong> {reply.text}
</div>
))}
</div>
)}
</div>
))}
</div>
);
}
Advanced List Rendering Patterns
đ Filtering and Searching
function SearchableList({ items }) {
const [searchTerm, setSearchTerm] = useState('');
const [filter, setFilter] = useState('all');
// Filter items based on search and filter criteria
const filteredItems = items.filter(item => {
const matchesSearch = item.name
.toLowerCase()
.includes(searchTerm.toLowerCase());
const matchesFilter = filter === 'all' || item.category === filter;
return matchesSearch && matchesFilter;
});
return (
<div>
{/* Search input */}
<input
type="text"
placeholder="Search items..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
{/* Filter dropdown */}
<select
value={filter}
onChange={(e) => setFilter(e.target.value)}
>
<option value="all">All Categories</option>
<option value="electronics">Electronics</option>
<option value="clothing">Clothing</option>
<option value="books">Books</option>
</select>
{/* Results count */}
<p>{filteredItems.length} items found</p>
{/* Render filtered items */}
{filteredItems.length > 0 ? (
<div className="items-grid">
{filteredItems.map(item => (
<ItemCard key={item.id} item={item} />
))}
</div>
) : (
<p>No items match your criteria.</p>
)}
</div>
);
}
đ Sorting and Grouping
function SortableList({ data }) {
const [sortBy, setSortBy] = useState('name');
const [sortOrder, setSortOrder] = useState('asc');
const [groupBy, setGroupBy] = useState('none');
// Sort the data
const sortedData = [...data].sort((a, b) => {
let aVal = a[sortBy];
let bVal = b[sortBy];
if (typeof aVal === 'string') {
aVal = aVal.toLowerCase();
bVal = bVal.toLowerCase();
}
if (sortOrder === 'asc') {
return aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
} else {
return aVal > bVal ? -1 : aVal < bVal ? 1 : 0;
}
});
// Group the data if needed
const groupedData = groupBy === 'none'
? { 'All Items': sortedData }
: sortedData.reduce((groups, item) => {
const group = item[groupBy];
if (!groups[group]) {
groups[group] = [];
}
groups[group].push(item);
return groups;
}, {});
return (
<div>
{/* Controls */}
<div className="controls">
<select
value={sortBy}
onChange={(e) => setSortBy(e.target.value)}
>
<option value="name">Sort by Name</option>
<option value="price">Sort by Price</option>
<option value="date">Sort by Date</option>
</select>
<button onClick={() => setSortOrder(prev =>
prev === 'asc' ? 'desc' : 'asc')}>
{sortOrder === 'asc' ? 'â' : 'â'}
</button>
<select
value={groupBy}
onChange={(e) => setGroupBy(e.target.value)}
>
<option value="none">No Grouping</option>
<option value="category">Group by Category</option>
<option value="status">Group by Status</option>
</select>
</div>
{/* Render grouped data */}
{Object.entries(groupedData).map(([groupName, items]) => (
<div key={groupName} className="group">
{groupBy !== 'none' && <h3>{groupName}</h3>}
<div className="items">
{items.map(item => (
<ItemCard key={item.id} item={item} />
))}
</div>
</div>
))}
</div>
);
}
đ Dynamic Lists with CRUD Operations
function DynamicTodoList() {
const [todos, setTodos] = useState([
{ id: 1, text: 'Learn React', completed: false },
{ id: 2, text: 'Build a project', completed: false }
]);
const [newTodo, setNewTodo] = useState('');
const [editingId, setEditingId] = useState(null);
const [editText, setEditText] = useState('');
// Create new todo
const addTodo = (e) => {
e.preventDefault();
if (newTodo.trim()) {
setTodos(prev => [...prev, {
id: Date.now(),
text: newTodo.trim(),
completed: false
}]);
setNewTodo('');
}
};
// Update todo
const updateTodo = (id, newText) => {
setTodos(prev => prev.map(todo =>
todo.id === id ? { ...todo, text: newText } : todo
));
setEditingId(null);
};
// Delete todo
const deleteTodo = (id) => {
setTodos(prev => prev.filter(todo => todo.id !== id));
};
// Toggle completion
const toggleComplete = (id) => {
setTodos(prev => prev.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
));
};
// Start editing
const startEditing = (id, text) => {
setEditingId(id);
setEditText(text);
};
return (
<div className="todo-app">
{/* Add new todo form */}
<form onSubmit={addTodo}>
<input
type="text"
value={newTodo}
onChange={(e) => setNewTodo(e.target.value)}
placeholder="Add a new todo..."
/>
<button type="submit">Add</button>
</form>
{/* Todo list */}
<ul className="todo-list">
{todos.map(todo => (
<li key={todo.id} className={todo.completed ? 'completed' : ''}>
{editingId === todo.id ? (
<div className="editing">
<input
type="text"
value={editText}
onChange={(e) => setEditText(e.target.value)}
onBlur={() => updateTodo(todo.id, editText)}
onKeyDown={(e) => {
if (e.key === 'Enter') {
updateTodo(todo.id, editText);
} else if (e.key === 'Escape') {
setEditingId(null);
}
}}
autoFocus
/>
</div>
) : (
<>
<span
className="todo-text"
onDoubleClick={() => startEditing(todo.id, todo.text)}
>
{todo.text}
</span>
<div className="todo-actions">
<button
onClick={() => toggleComplete(todo.id)}
className="toggle-btn"
>
{todo.completed ? 'âļ' : 'â'}
</button>
<button
onClick={() => startEditing(todo.id, todo.text)}
className="edit-btn"
>
âī¸
</button>
<button
onClick={() => deleteTodo(todo.id)}
className="delete-btn"
>
đī¸
</button>
</div>
</>
)}
</li>
))}
</ul>
{/* Summary */}
<div className="summary">
<p>
{todos.filter(t => !t.completed).length} of {todos.length} remaining
</p>
</div>
</div>
);
}
Best Practices
â Conditional Rendering
- Use && for simple show/hide logic
- Use ternary for either/or scenarios
- Use if/else for complex conditions
- Guard against null/undefined values
â List Rendering
- Always provide unique keys
- Use stable IDs, not array indices
- Extract list items into components
- Handle empty states gracefully
â Performance
- Memoize expensive computations
- Use React.memo for pure components
- Virtualize very long lists
- Avoid creating objects in render
â Common Mistakes
- Using array index as key
- Not handling loading/error states
- Complex logic in JSX
- Forgetting to handle empty arrays
What's Next?
You now understand how to conditionally render content and work with lists in React. These skills are fundamental to building dynamic, interactive applications. Next, you'll dive into React Hooks for more advanced state management.
đ Continue Learning
- React Hooks - Advanced state management with useEffect and custom hooks
- State Management - Managing complex application state
- Routing - Building single-page applications
- Transition Guide - Complete migration strategies