18 minLesson 18 of 40
React Fundamentals
Lists, Keys & Conditional Rendering
Lists, Keys & Conditional Rendering
Rendering lists and conditionally showing elements are the most common patterns in React. Getting them right — especially the key prop — prevents subtle bugs and poor performance.
Rendering Lists
const fruits = ["Apple", "Banana", "Cherry"];
// Basic list
function FruitList() {
return (
<ul>
{fruits.map((fruit, index) => (
<li key={index}>{fruit}</li>
))}
</ul>
);
}
// Object lists
const users = [
{ id: 1, name: "Alice", role: "admin" },
{ id: 2, name: "Bob", role: "user" }
];
function UserList() {
return (
<ul>
{users.map(user => (
<UserCard key={user.id} user={user} />
))}
</ul>
);
}
The key Prop
Keys help React identify which items changed, added, or removed. Without keys, React has to re-render the entire list on any change:
// ❌ Index as key — buggy when items reorder or are deleted
{items.map((item, index) => <Item key={index} item={item} />)}
// ✅ Stable unique ID as key
{items.map(item => <Item key={item.id} item={item} />)}
// When you have no ID, use a stable unique value
{tags.map(tag => <Tag key={tag.name} tag={tag} />)}
When index keys cause bugs:
// If you have a list with input fields and delete item[1]:
// With index keys: item[2] gets key=1, React thinks it's the same as old item[1]
// React reuses the DOM node — the input value doesn't reset!
// With ID keys: React correctly removes the old element and creates a new one
Empty and Loading States
function ProductList({ products, loading, error }) {
if (loading) {
return <div className="spinner">Loading products...</div>;
}
if (error) {
return <div className="error">Failed to load: {error.message}</div>;
}
if (products.length === 0) {
return (
<div className="empty">
<p>No products found.</p>
<button onClick={resetFilters}>Clear filters</button>
</div>
);
}
return (
<div className="product-grid">
{products.map(product => (
<ProductCard key={product.id} product={product} />
))}
</div>
);
}
Conditional Rendering Patterns
function Dashboard({ user }) {
// 1. Early return
if (!user) return <Navigate to="/login" />;
return (
<div>
{/* 2. && operator — renders right side when left is truthy */}
{user.notifications > 0 && (
<NotificationBadge count={user.notifications} />
)}
{/* 3. Ternary */}
{user.isPremium ? <PremiumFeatures /> : <UpgradeBanner />}
{/* 4. Nullish coalescing for defaults */}
<p>{user.bio ?? "No bio provided."}</p>
{/* 5. Switch via object map */}
{{ free: <FreePlan />, pro: <ProPlan />, enterprise: <EnterprisePlan /> }[user.plan]}
</div>
);
}
Filtering and Transforming Lists
function FilteredProductList({ products, searchQuery, category }) {
const filtered = products
.filter(p => !category || p.category === category)
.filter(p => p.name.toLowerCase().includes(searchQuery.toLowerCase()))
.sort((a, b) => a.name.localeCompare(b.name));
return (
<div>
<p>{filtered.length} products found</p>
{filtered.length > 0 ? (
<ul>
{filtered.map(p => (
<li key={p.id}>
<strong>{p.name}</strong> — ${p.price.toFixed(2)}
</li>
))}
</ul>
) : (
<p className="empty">No products match your search.</p>
)}
</div>
);
}
Fragments
When you need to return multiple elements without a wrapper div:
import { Fragment } from "react";
function TableRow({ user }) {
// You can't return two <td>s without wrapping in <tr>
// But the parent <tr> is already in the parent component
return (
<>
<td>{user.name}</td>
<td>{user.email}</td>
<td>{user.role}</td>
</>
);
}
// Fragment with key (for lists)
function GlossaryList({ items }) {
return (
<dl>
{items.map(item => (
<Fragment key={item.id}>
<dt>{item.term}</dt>
<dd>{item.definition}</dd>
</Fragment>
))}
</dl>
);
}
Next lesson: Forms in React — controlled components, validation, and submission.
📱
Get Notes Free →Get this course's notes on Telegram!
Free cheat sheets, summaries & practice exercises