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.

Vanilla JavaScript
JavaScript
// 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
React Conditional Rendering
React JSX
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.

Logical AND Pattern
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.

Ternary Pattern
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.

If/Else Pattern
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.

Switch Pattern
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.

Vanilla JavaScript
JavaScript
// 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
React List Rendering
React JSX
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
Key Examples
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

Filtered Lists
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

Sorted and Grouped Lists
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

Dynamic List Management
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

  1. React Hooks - Advanced state management with useEffect and custom hooks
  2. State Management - Managing complex application state
  3. Routing - Building single-page applications
  4. Transition Guide - Complete migration strategies