In Challenge 03 you built typed React components and learned how to pass props. Now you will practise two of the most common patterns in every real React codebase:
- Rendering a list of items from an array using
.map() - Conditional rendering — showing or hiding UI elements depending on data
By the end of this challenge your project board will handle any number of projects, display colour-coded status badges, warn when a project is overdue, and gracefully handle an empty list.
- Render a dynamic list with
.map()and supply a stablekeyprop. - Understand why React requires keys and what happens when you use array index.
- Use the
&&short-circuit operator and ternary expressions for conditional JSX. - Avoid the classic
0bug when using&&with falsy numbers. - Derive boolean flags from data (e.g.
isOverdue) inside a component. - Implement an empty state that renders when the data array is empty.
The start/ directory contains the app from Challenge 03's solution. It already:
- Renders three
ProjectCardcomponents hardcoded inMainContent.tsx - Has typed props (
ProjectCardProps,NavItem) - Shows a conditional
dueDateinside each card
The app works — run it with npm install && npm run dev to verify — but the
rendering is not yet driven by the array.
Work inside start/src/. You may edit any file.
Open src/components/MainContent.tsx. The projects array currently has 3 items.
Add at least 2 more so there are 5 projects total with a mix of statuses:
- At least one
"active", one"completed", one"archived" - At least two projects that have a
dueDatein the past (before today)
Use the id strings "proj-1" through "proj-5" so they are meaningful.
Replace the three individual <ProjectCard … /> instances with a single .map()
call. Use project.id as the key — never use array index as key for lists
that can change.
{projects.map((project) => (
<ProjectCard key={project.id} {...project} />
))}Open src/components/ProjectCard.tsx. Add logic to determine whether the project
is overdue:
const isOverdue =
dueDate !== undefined && new Date(dueDate) < new Date();Then render a small warning only when isOverdue is true:
{isOverdue && <span className="project-card__overdue">⚠ Overdue</span>}Back in MainContent.tsx, add a conditional block that shows a friendly message
when projects.length === 0:
{projects.length === 0 ? (
<p className="empty-state">No projects yet. Create your first project!</p>
) : (
<div className="project-grid">
{projects.map((project) => (
<ProjectCard key={project.id} {...project} />
))}
</div>
)}You can temporarily set projects to [] to verify the empty state renders.
The CSS classes status-badge--active, status-badge--completed, and
status-badge--archived are already defined in App.css. Confirm all three
statuses display with the correct colour by looking at your 5-project list.
- There are 5 or more projects in the array, with all three statuses present
- The list renders using
.map()withkey={project.id} - Projects with a
dueDatebefore today show "⚠ Overdue" - Projects without a
dueDate, or with a futuredueDate, do not show the indicator - Setting
projectsto[]shows the empty state message - Status badges are green (active), blue (completed), and grey (archived)
- No TypeScript errors (
npm run buildpasses)
React uses the key prop to identify which items changed, were added, or were
removed during re-renders. A stable, unique key (like an id) keeps component
state correctly associated with the right item. Using array index is only safe for
static, non-reorderable lists.
// BUG — renders "0" when taskCount is 0
{taskCount && <span>{taskCount} tasks</span>}
// SAFE — explicit boolean check
{taskCount > 0 && <span>{taskCount} tasks</span>}When the left-hand side of && is a falsy non-boolean (0, "", NaN), React
renders it as text. Always convert to a proper boolean first.
You do not need extra state or props to compute isOverdue. Derive it directly
from the dueDate prop:
const isOverdue = dueDate !== undefined && new Date(dueDate) < new Date();cd start
npm install
npm run devNavigate to http://localhost:5173 in your browser.
To check the TypeScript build:
npm run build