Learn ReactJS - Conditional Rendering & Lists
Episode 7 of 24

Learn ReactJS - Conditional Rendering & Lists

This episode teaches conditional rendering with ternary and logical operators, then rendering lists with stable keys. You'll also learn about Fragments, portals, and error boundaries, plus best practices for building truly dynamic UI.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

Real apps rarely display the same thing over and over. Sometimes you show a loading spinner, sometimes a list of items, sometimes an empty message. Sometimes you need to render dozens of items from an array. Those are the two skills episode 7 covers: conditional rendering and rendering lists.

We'll also look at Fragments to group elements without extra tags, portals to render outside the DOM hierarchy, and error boundaries so a single error doesn't bring down the entire app. These are important parts of dynamic UI that feels professional.

Conditional Rendering with Ternary and Logical Operators

Ternary: Two Clear Branches

The ternary expression is best suited for choosing between two views:

JSTernary for login state
function Status({ masuk }) {
  return (
    <div>
      {masuk ? <p>Selamat datang kembali</p> : <button>Login</button>}
    </div>
  )
}

{masuk ? <p>...</p> : <button>...</button>} renders the paragraph if masuk is true, and the button if false. Ternary expressions can be nested, but it's better to split them into small components if they start getting deeply nested.

Logical Operators: Show or Hide

To display something only when the condition is true (without an else branch), use &&:

JSLogical AND for a notice
function Notifikasi({ pesan }) {
  return (
    <div>
      {pesan.length > 0 && <p className="notice">{pesan}</p>}
    </div>
  )
}

{pesan.length > 0 && <p>...{:javascript}</p>} renders the element only if the left-hand expression is truthy. Be careful with numbers: {jumlah && <p>...}</p> will render 0 if jumlah is zero — use an explicit condition like jumlah > 0.

Rendering Lists with Stable Keys

Using map to Render a List

The way to render an array is with map, which returns JSX elements:

JSRender a list from an array
function DaftarProduk({ produkList }) {
  return (
    <ul>
      {produkList.map((produk) => (
        <li key={produk.id}>{produk.nama} - Rp{produk.harga}</li>
      ))}
    </ul>
  )
}

produkList.map((produk) => <li key={produk.id}>...{:javascript}</li>) turns each array item into a list element. Notice the key prop, which is required for each item.

Stable and Unique Keys

key helps React identify which items changed, were added, or were removed. The rules:

  • Use a unique ID from the data, not the array index, if the data can be sorted or changed.
  • The key must stay stable across renders for the same item.
  • Never use random values that change on every render.

Using the index as a key causes bugs when items are inserted or removed in the middle of a list — an item's state can end up connected to the wrong item.

Fragments, Portals, and Error Boundaries

Fragments Without Extra Tags

Sometimes you need several elements at once without wrapping them in a div. Fragments solve this:

JSShorthand fragment
function Detail() {
  return (
    <>
      <h2>Judul</h2>
      <p>Paragraf pertama</p>
      <p>Paragraf kedua</p>
    </>
  )
}

<>...</> is the shorthand syntax for React.Fragment. Useful when filling CSS grids or tables that can't have an extra wrapper element.

Portals to Render Outside the Hierarchy

A portal renders children into a different DOM node than their parent. Modals, tooltips, and dropdowns are the best fit for this:

JSA modal using a portal
import { createPortal } from "react-dom"
 
function Modal({ anak }) {
  return createPortal(
    <div className="overlay">{anak}</div>,
    document.getElementById("modal-root")
  )
}

createPortal(jsx, document.getElementById("modal-root")) renders the content into a modal-root element separate from #root. Add <div id="modal-root"></div> to index.html so the target element always exists.

Error Boundaries for Render Safety

Error boundaries catch render errors in child components so they don't wipe out the entire app. Since there's no hook for this yet, they have to be class components:

JSA basic error boundary
class ErrorBoundary extends React.Component {
  state = { error: null }
 
  static getDerivedStateFromError(error) {
    return { error }
  }
 
  render() {
    if (this.state.error) {
      return <p>Terjadi kesalahan: {this.state.error.message}</p>
    }
    return this.props.children
  }
}

static getDerivedStateFromError(error) stores the error in state, then render shows a fallback UI instead of a blank page. Wrap error-prone areas with <ErrorBoundary>...</ErrorBoundary>.

Best Practices for Dynamic UI

A few habits that keep dynamic UI healthy:

  • Split rendering into small components so conditions don't pile up.
  • Give state clear defaults: empty, loading, success, and error.
  • Always provide a fallback for empty lists, not just list elements.
  • Combine conditional rendering with the loading and error components from episode 8.
An example of an empty list
npm run dev

Try rendering DaftarProduk with produkList=[] — the empty list shows nothing. Add a condition like {produkList.length === 0 && <p>Belum ada produk</p>} for a better experience.

Conclusion

Episode 7 completed the dynamic UI foundation: conditional rendering with ternary and &&, rendering lists with stable keys, Fragments, portals for modals, and error boundaries so one error doesn't collapse the app.

Key takeaways:

  • Ternary for two branches; && for showing or hiding.
  • map renders lists; always include a stable, unique key.
  • Don't use the array index as a key when the list order can change.
  • Fragments <>...</> group elements without an extra DOM node.
  • Portals render outside the hierarchy: modals and tooltips.
  • Error boundaries ensure a single error doesn't blank the whole page.

In the next episode, episode 8, we'll cover data fetching & asynchronous UI — fetching data with fetch, axios, and async/await, managing loading and error states, stable Suspense patterns, and caching with React Query or SWR. Your app starts communicating with the real world.