React: Conditional Rendering and List Rendering

Last updated: 2026-08-26

Conditional rendering is like traffic lights—it determines what to display based on the state; list rendering is like assembly-line production—a list of data that generates identical UI components in batches.


1. What You'll Learn



2. The Front-End Story of an E-Commerce Platform

(1) Pain Point: The 6 States of a Product List

Alice is working on a product list page for an e-commerce platform. This page has six display states, and each time the state changes, she has to manually manipulate the DOM:

JAVASCRIPT
function renderProductList(state) {
  const container = document.getElementById('product-list')
  container.innerHTML = ''

  if (state.isLoading) {
    container.innerHTML = '<div class="loading">Loading......</div>'
  } else if (state.error) {
    container.innerHTML = '<div class="error">Failed to load</div>'
  } else if (!state.isLoggedIn) {
    container.innerHTML = '<div>Please log in</div>'
  } else if (state.products.length === 0) {
    container.innerHTML = '<div>No products available</div>'
  } else {
    let html = ''
    state.products.forEach(p => {
      html += `<div class="card"><h3>${p.name}</h3><p>$${p.price}</p></div>`
    })
    container.innerHTML = html
  }
}

Alice's question: Every time the state changes, I have to manually clear the container and construct the HTML. 30 lines of code do just one thing—display the current state.

(2) Solutions for Conditional Rendering in React

JSX
function ProductList({ user, products, isLoading, error }) {
  // Each state is independent if Branch,No interference with one another
  if (isLoading) return <LoadingSpinner />
  if (error) return <ErrorMessage message={error} />
  if (!user) return <LoginPrompt />
  if (products.length === 0) return <EmptyState />
  
  return (
    <div className="product-grid">
      {products.map(product => (
        <ProductCard key={product.id} product={product} />
      ))}
    </div>
  )
}
▶ Try it Yourself

Benefits: Just 15 lines of code cover all states, and each branch is easy to understand at a glance.



3. Three Ways to Use Conditional Rendering

Method Syntax Use Cases Notes
Ternary operator condition ? A : B Choose one of two (with an "else" branch) Nested ternary operators are hard to read; avoid more than two levels
Logic and && condition && A Display only if condition is met (no "else") 0 && <Comp /> will render 0; use > 0
Logical OR ` ` `value

(1) Ternary Operator ? :

Scenarios suitable for "choosing one of two options":

JSX
function StatusBadge({ isActive }) {
  return (
    <span style={{ color: isActive ? '#52c41a' : '#ff4d4f' }}>
      {isActive ? '🟢 Online' : '🔴 Offline'}
    </span>
  )
}

// isActive=true  → <span>🟢 Online</span>
// isActive=false → <span>🔴 Offline</span>
▶ Try it Yourself

(2) Logic and &&

Suitable for "Display only if conditions are met; otherwise, do not display":

JSX
function Notification({ count }) {
  return (
    <div className="notification-icon">
      🔔
      {/* count > 0 A red dot appears,Otherwise, nothing will be displayed. */}
      {count > 0 && (
        <span className="badge">{count > 99 ? '99+' : count}</span>
      )}
    </div>
  )
}

// count=0  → Show only 🔔
// count=5  → Show 🔔 and  "5"
▶ Try it Yourself
⚠️ Pitfall: Do not write {items.length && <List />}. When items.length is 0, the result of 0 && <List /> is 0, and React will render 0 on the page. The correct way to write this is {items.length > 0 && <List />}.

(3) Logical OR ||

Suitable for "providing a fallback value":

JSX
function UserProfile({ user }) {
  return (
    <div>
      <h2>{user.nickname || 'Anonymous User'}</h2>
      <p>{user.bio || 'This person is lazy.,Nothing was written...'}</p>
      <p>Location:{user.location || 'Unknown'}</p>
    </div>
  )
}
▶ Try it Yourself

▶ Example: Side-by-Side Comparison of Three Types of Conditional Rendering

Output:

TEXT 📖 Display only
Side-by-side comparison: HTML syntax vs JSX syntax showing equivalent markup. TypeScript-typed React component with interface props
JSX
// ============================================
// Example:Combined Use of Three Types of Conditional Rendering in the User Panel
// ============================================

function UserPanel({ user }) {
  return (
    <div className="panel">
      {/* 1. Ternary:Choose one of the two */}
      <h2>
        {user.isVIP ? '⭐ Premium Member' : 'Regular User'}
      </h2>

      {/* 2. &&:Display only when met */}
      {user.isVIP && (
        <p>Membership Expiration:{user.vipExpireDate}</p>
      )}

      {/* 3. ||:Floor value */}
      <p>Signature:{user.slogan || 'This person is really cool.,No signature'}</p>

      {/* Combined Use:List of Tags */}
      {user.tags && user.tags.length > 0 && (
        <div>
          {user.tags.map(tag => (
            <span key={tag} style={{ background: '#f0f0f0', padding: '2px 8px', margin: '2px', borderRadius: '4px' }}>
              {tag}
            </span>
          ))}
        </div>
      )}
    </div>
  )
}
▶ Try it Yourself

Output:

TEXT 📖 Display only
If VIP: "⭐ Premium Member" + membership expiry. If not: "Regular User". Signature: user text or fallback. Tags shown only if present.


4. List Rendering: .map()

(1) Basic Usage

Array.map() Iterate through an array and return a JSX for each element:

JSX
const fruits = ['Apple', 'Banana', 'Orange', 'Grapes']

function FruitList() {
  return (
    <ul>
      {fruits.map(fruit => (
        <li>{fruit}</li>
      ))}
    </ul>
  )
}
// Output:
// <ul>
//   <li>Apple</li>
//   <li>Banana</li>
//   <li>Orange</li>
//   <li>Grapes</li>
// </ul>
▶ Try it Yourself

(2) Array of rendering objects

JSX
function ScoreTable() {
  const students = [
    { id: 1, name: 'Alice', score: 92 },
    { id: 2, name: 'Bob', score: 85 },
    { id: 3, name: 'Charlie', score: 78 }
  ]

  return (
    <table border="1" cellPadding="8">
      <thead>
        <tr><th>Name</th><th>Fractions</th><th>Level</th></tr>
      </thead>
      <tbody>
        {students.map(s => (
          <tr key={s.id}>
            <td>{s.name}</td>
            <td>{s.score}</td>
            <td>{s.score >= 90 ? 'Excellent' : s.score >= 80 ? 'Good' : 'Passing Grade'}</td>
          </tr>
        ))}
      </tbody>
    </table>
  )
}
▶ Try it Yourself

(3) 3 Data Transformation Techniques for List Rendering

JSX
const items = [
  { id: 1, name: 'React Basics', price: 0 },
  { id: 2, name: 'Vue Getting Started', price: 0 },
  { id: 3, name: 'Node.js Real-World Experience', price: 99 }
]

// 1. Direct Rendering
{items.map(item => <li key={item.id}>{item.name}</li>)}

// 2. Filter First, Then Render(Free Courses)
{items
  .filter(item => item.price === 0)
  .map(item => <li key={item.id}>{item.name}(Free)</li>)
}

// 3. Extract Rendering Functions
function renderItem(item) {
  return (
    <li key={item.id}>
      {item.name} - ${item.price}
    </li>
  )
}
{items.map(renderItem)}
▶ Try it Yourself

5. The Importance of the Key

React uses key to identify each element in a list. When the key remains constant, React reuses the DOM node; when the key changes, React destroys and recreates it.

100%
graph LR
    A[List Changes] --> B{key Changes?}
    B -->|No, key The same| C[Reuse DOM Node<br/>Update only the changes]
    B -->|Yes, key It's changed| D[Delete Old Nodes<br/>Create a New Node]
    B -->|No, None key| E[Render All Over]
Key Strategy Performance Recommendation
Database Unique ID ✅ Optimal ⭐⭐⭐
Unique string (UUID) ✅ Best ⭐⭐⭐
Array index ⚠️ Risky (state may become inconsistent when the list changes)
Random number ❌ Range (varies with each render)
No key passed ❌ Poor performance

▶ Example: With a key vs. Without a key

Output:

TEXT 📖 Display only
Subheading: "        {user.isVIP ? '⭐ Premium Member' : 'Regular User'}      ". Displays: "0 && ("
JSX
// ============================================
// Example:key Impact on the Input Field's State
// ============================================

function KeyDemo() {
  const [items, setItems] = React.useState([
    { id: 'a', text: 'Task A' },
    { id: 'b', text: 'Task B' },
    { id: 'c', text: 'Task C' }
  ])

  function shuffle() {
    setItems([...items].sort(() => Math.random() - 0.5))
  }

  return (
    <div>
      <button onClick={shuffle}>Randomize the order</button>
      <p>✅ has  key(id Stable,The content in the input field remains intact):</p>
      <ul>
        {items.map(item => (
          <li key={item.id}>
            {item.text} <input placeholder="Enter content" />
          </li>
        ))}
      </ul>
      
      <p>❌ Without key(or  index As key,The text in the input field appears jumbled):</p>
      <ul>
        {items.map((item, index) => (
          <li key={index}>
            {item.text} <input placeholder="The content may be misaligned" />
          </li>
        ))}
      </ul>
    </div>
  )
}
// Click"Randomize the order"after ,has  key The content in the list input field remains in order
// use  index List of,The content in the input field and the text are misaligned
▶ Try it Yourself

Output:

TEXT 📖 Display only
Two lists with input fields. Click "Randomize": ✅ list (key=id) keeps input values aligned; ❌ list (key=index) input values misalign with text


6. Empty State and Loading State

Status Condition UI Behavior Handling
Loading loading === true Spinner / Skeleton Screen if (loading) return <Spinner />
Loading Failed error !== null Error Message + Retry Button if (error) return <ErrorView />
Empty Data data.length === 0 Empty State Illustration + Copy if (!data.length) return <Empty />
Normal None of the above Render data list Default: return to normal UI

In real-world projects, there are three states that must be handled when loading data:

JSX
// ============================================
// Example:Complete List Component(3 Handling Different States)
// ============================================

function ArticleList() {
  const [articles, setArticles] = React.useState([])
  const [isLoading, setIsLoading] = React.useState(true)
  const [error, setError] = React.useState(null)

  // Simulated Loading(Actually, it is API Call)
  React.useEffect(() => {
    fetch('/api/articles')
      .then(res => {
        if (!res.ok) throw new Error('Network request failed')
        return res.json()
      })
      .then(data => {
        setArticles(data)
        setIsLoading(false)
      })
      .catch(err => {
        setError(err.message)
        setIsLoading(false)
      })
  }, [])

  // 1. Loading...
  if (isLoading) {
    return (
      <div style={{ textAlign: 'center', padding: '40px' }}>
        <div className="spinner" />
        <p>Loading article...</p>
      </div>
    )
  }

  // 2. Failed to load
  if (error) {
    return (
      <div style={{ textAlign: 'center', padding: '40px', color: '#ff4d4f' }}>
        <p>❌ Failed to load:{error}</p>
        <button onClick={() => window.location.reload()}>Reload</button>
      </div>
    )
  }

  // 3. Empty data
  if (articles.length === 0) {
    return (
      <div style={{ textAlign: 'center', padding: '40px', color: '#999' }}>
        <p>📭 No articles available</p>
        <p>No one has posted an article yet.,Go ahead and write your first one!!</p>
      </div>
    )
  }

  // 4. Normal Rendering
  return (
    <div>
      <h3>List of Articles(Total  {articles.length}  articles)</h3>
      {articles.map(article => (
        <ArticleCard key={article.id} article={article} />
      ))}
    </div>
  )
}


JSX
// ============================================
// Complete Example:Product List(Conditional Rendering + Comprehensive Guide to List Rendering)
// Features:Filter by Category、Search、Prompt for empty results
// ============================================

function ShopPage() {
  const [products] = React.useState([
    { id: 1, name: 'Wireless Mouse', category: 'Electronics', price: 89, sales: 2000 },
    { id: 2, name: 'Mechanical Keyboard', category: 'Electronics', price: 299, sales: 1500 },
    { id: 3, name: 'Notebook', category: 'Office', price: 15, sales: 5000 },
    { id: 4, name: 'Office Chair', category: 'Furniture', price: 899, sales: 800 },
    { id: 5, name: 'Desk Lamp', category: 'Furniture', price: 129, sales: 3000 },
    { id: 6, name: 'Monitor', category: 'Electronics', price: 1599, sales: 600 }
  ])
  const [search, setSearch] = React.useState('')
  const [category, setCategory] = React.useState('All')

  // Filtering Logic
  const filtered = products.filter(p => {
    const matchSearch = p.name.includes(search)
    const matchCategory = category === 'All' || p.category === category
    return matchSearch && matchCategory
  })

  // Get the list of categories(Remove duplicates)
  const categories = ['All', ...new Set(products.map(p => p.category))]

  return (
    <div style={{ maxWidth: '800px', margin: '0 auto' }}>
      <h2>🛒 Product List</h2>
      
      {/* Search box */}
      <input
        value={search}
        onChange={e => setSearch(e.target.value)}
        placeholder="Search by Product Name..."
        style={{ width: '100%', padding: '8px', marginBottom: '12px', border: '1px solid #d9d9d9', borderRadius: '4px' }}
      />

      {/* Category Button(List Rendering) */}
      <div style={{ marginBottom: '12px' }}>
        {categories.map(cat => (
          <button
            key={cat}
            onClick={() => setCategory(cat)}
            style={{
              padding: '6px 16px', margin: '0 4px',
              backgroundColor: category === cat ? '#1890ff' : '#f0f0f0',
              color: category === cat ? 'white' : '#333',
              border: 'none', borderRadius: '4px', cursor: 'pointer'
            }}
          >
            {cat}
          </button>
        ))}
      </div>

      {/* Results Summary */}
      {search || category !== 'All' ? (
        <p style={{ color: '#666' }}>
          {search && `Search"${search}" `}
          {category !== 'All' && `/${category} `}
          Total  {filtered.length} Items
        </p>
      ) : null}

      {/* Empty result */}
      {filtered.length === 0 ? (
        <div style={{ textAlign: 'center', padding: '40px', color: '#999' }}>
          <p>😕 No matching products were found.</p>
          <button
            onClick={() => { setSearch(''); setCategory('All') }}
            style={{ padding: '8px 16px', marginTop: '8px', cursor: 'pointer' }}
          >
            Clear All Filters
          </button>
        </div>
      ) : (
        /* Product Grid(List Rendering) */
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '16px' }}>
          {filtered.map(product => (
            <div key={product.id} style={{ border: '1px solid #eee', borderRadius: '8px', padding: '16px' }}>
              <div style={{ height: '120px', backgroundColor: '#f5f5f5', borderRadius: '4px', marginBottom: '8px' }} />
              <h4>{product.name}</h4>
              <p style={{ color: '#ff4d4f', fontWeight: 'bold', fontSize: '18px' }}>${product.price}</p>
              <p style={{ color: '#999', fontSize: '13px' }}>Sold {product.sales}  items</p>
              <span style={{ background: '#f0f5ff', color: '#1890ff', padding: '2px 8px', borderRadius: '4px', fontSize: '12px' }}>
                {product.category}
              </span>
            </div>
          ))}
        </div>
      )}
    </div>
  )
}

Expected Output: A complete product list page that supports search filtering, category filtering, empty result notifications, and statistics.


▶ Example 5: Dynamic Form Field Rendering

Output:

TEXT 📖 Display only
Displays: "Randomize the order". Button: Randomize the order. Input: Enter content, The content may be misaligned
JSX
function DynamicForm() {
  const [formType, setFormType] = useState('personal')
  const [formData, setFormData] = useState({
    name: '',
    email: '',
    company: '',
    taxId: '',
    budget: '',
  })

  function handleChange(e) {
    const { name, value } = e.target
    setFormData(prev => ({ ...prev, [name]: value }))
  }

  const personalFields = (
    <>
      <input name="name" value={formData.name} onChange={handleChange} placeholder="Name" />
      <input name="email" value={formData.email} onChange={handleChange} placeholder="Email" />
    </>
  )

  const businessFields = (
    <>
      <input name="company" value={formData.company} onChange={handleChange} placeholder="Company" />
      <input name="taxId" value={formData.taxId} onChange={handleChange} placeholder="Tax ID" />
      <input name="budget" value={formData.budget} onChange={handleChange} placeholder="Budget ($)" />
    </>
  )

  return (
    <div style={{ maxWidth: 400, margin: '0 auto' }}>
      <h3>Registration</h3>
      <div style={{ marginBottom: 12 }}>
        {['personal', 'business'].map(type => (
          <button
            key={type}
            onClick={() => setFormType(type)}
            style={{
              padding: '6px 16px', marginRight: 8, cursor: 'pointer',
              backgroundColor: formType === type ? '#1890ff' : '#f0f0f0',
              color: formType === type ? 'white' : '#333', border: 'none', borderRadius: 4,
            }}
          >
            {type === 'personal' ? 'Personal' : 'Business'}
          </button>
        ))}
      </div>
      {formType === 'personal' ? personalFields : businessFields}
      <button style={{ marginTop: 12, padding: '8px 24px', cursor: 'pointer' }}>Submit</button>
    </div>
  )
}

Output:

TEXT 📖 Display only
Personal form: Name + Email fields. Business form: Company + Tax ID + Budget fields. Toggle via buttons. Submit at bottom.

▶ Example 3: Searching, Filtering, and Sorting Lists

Output:

TEXT 📖 Display only
Search input filters items. Sort buttons: A-Z (by name) / Stars (by rating). Active sort highlighted blue. Filtered + sorted list updates reactively
JSX
function FilterableList() {
  const [items] = useState([
    { id: 1, name: 'React', category: 'Frontend', stars: 220 },
    { id: 2, name: 'Django', category: 'Backend', stars: 78 },
    { id: 3, name: 'Vue', category: 'Frontend', stars: 207 },
    { id: 4, name: 'Express', category: 'Backend', stars: 63 },
    { id: 5, name: 'Tailwind', category: 'CSS', stars: 80 },
  ])
  const [search, setSearch] = useState('')
  const [sortKey, setSortKey] = useState('name')

  const filtered = items
    .filter(i => i.name.toLowerCase().includes(search.toLowerCase()))
    .sort((a, b) => sortKey === 'stars' ? b.stars - a.stars : a.name.localeCompare(b.name))

  return (
    <div style={{ maxWidth: 400, margin: '0 auto' }}>
      <input value={search} onChange={e => setSearch(e.target.value)}
        placeholder="Search frameworks..." style={{ width: '100%', padding: 8, borderRadius: 4, marginBottom: 8 }} />
      <div style={{ marginBottom: 8 }}>
        <button onClick={() => setSortKey('name')} style={{ padding: '4px 12px', marginRight: 4, cursor: 'pointer', background: sortKey === 'name' ? '#1890ff' : '#f0f0f0', color: sortKey === 'name' ? 'white' : '#333', border: 'none', borderRadius: 4 }}>A-Z</button>
        <button onClick={() => setSortKey('stars')} style={{ padding: '4px 12px', cursor: 'pointer', background: sortKey === 'stars' ? '#1890ff' : '#f0f0f0', color: sortKey === 'stars' ? 'white' : '#333', border: 'none', borderRadius: 4 }}>Stars</button>
      </div>
      {filtered.length === 0 ? (
        <p style={{ color: '#999', textAlign: 'center' }}>No results</p>
      ) : (
        <ul style={{ listStyle: 'none', padding: 0 }}>
          {filtered.map(i => <li key={i.id} style={{ padding: 8, borderBottom: '1px solid #f0f0f0', display: 'flex', justifyContent: 'space-between' }}><span>{i.name} <small style={{ color: '#999' }}>({i.category})</small></span><span>⭐ {i.stars}k</span></li>)}
        </ul>
      )}
    </div>
  )
}
▶ Try it Yourself

Output:

TEXT 📖 Display only
Frameworks: React ⭐220k, Vue ⭐207k, Tailwind ⭐80k, Django ⭐78k, Express ⭐63k. Search filters; A-Z/Stars sort toggles.

▶ Example 4: Rendering Nested Lists—Displaying Categorized Products

Output:

TEXT 📖 Display only
Displays "{cat.name}"
JSX
function CategorizedProducts() {
  const categories = [
    { id: 'electronics', name: 'Electronics', products: [
      { id: 1, name: 'Keyboard', price: 79 },
      { id: 2, name: 'Monitor', price: 399 },
    ]},
    { id: 'furniture', name: 'Furniture', products: [
      { id: 3, name: 'Desk', price: 249 },
      { id: 4, name: 'Chair', price: 189 },
    ]},
    { id: 'books', name: 'Books', products: [] },
  ]

  return (
    <div style={{ maxWidth: 500, margin: '0 auto' }}>
      {categories.map(cat => (
        <div key={cat.id} style={{ marginBottom: 16 }}>
          <h3 style={{ borderBottom: '2px solid #1890ff', paddingBottom: 4 }}>{cat.name}</h3>
          {cat.products.length === 0 ? (
            <p style={{ color: '#999', fontStyle: 'italic', paddingLeft: 16 }}>No products yet</p>
          ) : (
            <ul style={{ listStyle: 'none', padding: 0, paddingLeft: 16 }}>
              {cat.products.map(p => (
                <li key={p.id} style={{ padding: 4, display: 'flex', justifyContent: 'space-between' }}>
                  <span>{p.name}</span><span>${p.price}</span>
                </li>
              ))}
            </ul>
          )}
        </div>
      ))}
    </div>
  )
}
▶ Try it Yourself

Output:

TEXT 📖 Display only
Electronics: Keyboard $79, Monitor $399
Furniture: Desk $249, Chair $189
Books: "No products yet" (italicized empty state)

❓ FAQ

Q Can .map() be nested? How are keys handled when using multiple .map() calls?
A Yes, it can be nested. Each level of .map() must have its own key. For example, rendering "Category → Product: cats.map(c => <div key={c.id}><h3>{c.name}</h3>{c.products.map(p => <span key={p.id}>{p.name}</span>)}</div>)". Keys at different levels can be the same (as long as they are unique within their respective arrays).
Q Which is better—ternary expressions or the && syntax?
A Each has its own use case: && is suitable for “display if the condition is met; render nothing if not”; ternary ? : is suitable for “display component X if condition A is met; display component Y if condition B is met.” If you want to “display nothing when the condition isn’t met” using the ternary operator, the syntax would be condition ? <Comp /> : null, but && is more concise in this case. However, be aware of the “0 rendering trap” with &&0 && <Comp /> will render a 0.

📖 Summary


📝 Exercises

  1. Basic Problem (Difficulty ⭐): Create a TemperatureDisplay component that, based on the temp props, displays 🔥 in red when the temperature is >35°C, ☀️ in yellow when it is 15–35°C, and ❄️ in blue when it is <15°C.
  2. Advanced Problem (Difficulty ⭐⭐): Create a TodoFilter component that accepts two props—todos (an array of tasks) and filter (all/completed/uncompleted)—and implement filtering based on state.
  3. Challenge (Difficulty: ⭐⭐⭐): Create a Pagination component that accepts totalPages, currentPage, and onPageChange props and renders page number buttons, supporting the first and last pages as well as an ellipsis.
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏