Runnable lesson · press Try it on the examples to edit and run them here.
Try each exercise before opening the solution. Press Try it on a solution to run it, then change it. Each exercise uses ideas from the first half of the course.
Exercise: 1. Profile cards from data
Make a ProfileCard component with the props name and role. Render one card for each person in this array, with the person’s id as the key:
[{ id: 1, name: "Ada", role: "Engineer" }, { id: 2, name: "Grace", role: "Admiral" }]
Hint: a component per item, map in the parent. See Rendering Lists. —solution
function ProfileCard({ name, role }) {
return (
<article style={{ border: "1px solid #ccc", borderRadius: 8, padding: 12, marginBottom: 8 }}>
<h3 style={{ margin: 0 }}>{name}</h3>
<p style={{ margin: 0 }}>{role}</p>
</article>
);
}
const people = [
{ id: 1, name: "Ada", role: "Engineer" },
{ id: 2, name: "Grace", role: "Admiral" },
];
function App() {
return (
<div>
{people.map((p) => <ProfileCard key={p.id} name={p.name} role={p.role} />)}
</div>
);
}
Exercise: 2. A role badge
Show the text “Admin” in a coloured badge next to the person’s name when role === "admin", and nothing otherwise. Render three users: one admin, two others.
Hint: && or a ternary. Remember not to render 0 by accident (Conditional Rendering). —solution
function User({ name, role }) {
return (
<li>
{name}{" "}
{role === "admin" && (
<span style={{ background: "#b71c1c", color: "#fff", borderRadius: 4, padding: "0 6px", fontSize: 12 }}>Admin</span>
)}
</li>
);
}
function App() {
return (
<ul>
<User name="Ada" role="admin" />
<User name="Grace" role="editor" />
<User name="Linus" role="viewer" />
</ul>
);
}
Exercise: 3. Highlight low grades
Show a list of students with their score. If the score is below 50, show it in red and add the word “Retake” after it.
Hint: compute const failing = score < 50 and use it in the style and the text. —solution
const students = [
{ id: 1, name: "Ada", score: 92 },
{ id: 2, name: "Bob", score: 41 },
{ id: 3, name: "Cleo", score: 67 },
];
function App() {
return (
<ul>
{students.map(({ id, name, score }) => {
const failing = score < 50;
return (
<li key={id} style={{ color: failing ? "#b71c1c" : "inherit" }}>
{name}: {score} {failing && <strong>Retake</strong>}
</li>
);
})}
</ul>
);
}
Exercise: 4. A button with defaults
Write a Button component with props label (default “Click me”) and variant (default “primary” or “secondary”). Use it three ways: with no props, with only a label, and with both.
Hint: default values in the parameter destructuring (Default Prop Values). —solution
function Button({ label = "Click me", variant = "primary" }) {
const style = variant === "primary"
? { background: "#1a5276", color: "#fff", border: "1px solid #1a5276" }
: { background: "#fff", color: "#1a5276", border: "1px solid #1a5276" };
return <button style={{ ...style, padding: "6px 14px", borderRadius: 6 }}>{label}</button>;
}
function App() {
return (
<div style={{ display: "flex", gap: 8 }}>
<Button />
<Button label="Save" />
<Button label="Cancel" variant="secondary" />
</div>
);
}
Exercise: 5. A Panel with children
Create a Panel component that receives a title and children, and draws a bordered box with the title as a heading. Use it with a paragraph in one panel and a list in another.
Hint: The children Prop. —solution
function Panel({ title, children }) {
return (
<section style={{ border: "1px solid #ccc", borderRadius: 8, padding: 12, marginBottom: 12 }}>
<h3 style={{ marginTop: 0 }}>{title}</h3>
{children}
</section>
);
}
function App() {
return (
<div>
<Panel title="About"><p>I am learning React.</p></Panel>
<Panel title="Skills">
<ul><li>HTML</li><li>CSS</li><li>React</li></ul>
</Panel>
</div>
);
}
Exercise: 6. Table rows with a Fragment
PriceRow should return two table cells (<td>) for a product name and price, with no extra wrapper element. Use it inside a <table> for two products.
Hint: Fragments. —solution
function PriceRow({ name, price }) {
return (
<>
<td>{name}</td>
<td>${price}</td>
</>
);
}
function App() {
return (
<table>
<thead><tr><th align="left">Product</th><th align="left">Price</th></tr></thead>
<tbody>
<tr><PriceRow name="Keyboard" price={49} /></tr>
<tr><PriceRow name="Mouse" price={25} /></tr>
</tbody>
</table>
);
}
Exercise: 7. A coloured tag
Tag takes a color prop (any CSS colour) and children. Show three tags with different colours. Use an object for the style.
Hint: JSX Attributes. —solution
function Tag({ color = "#555", children }) {
return (
<span style={{ background: color, color: "#fff", padding: "2px 10px", borderRadius: 999, marginRight: 6, fontSize: 13 }}>
{children}
</span>
);
}
function App() {
return (
<p>
<Tag color="#1a5276">react</Tag>
<Tag color="#6c3483">css</Tag>
<Tag color="#b9770e">html</Tag>
</p>
);
}
Exercise: 8. A layout with slots
Layout has props header, sidebar and children. Place them in a grid: header on top across both columns, a sidebar on the left and the main content on the right. Fill the slots with simple elements.
Hint: Component Composition. —solution
function Layout({ header, sidebar, children }) {
return (
<div style={{ display: "grid", gridTemplateColumns: "140px 1fr", gap: 12 }}>
<header style={{ gridColumn: "1 / -1", background: "#eaf2f8", padding: 8 }}>{header}</header>
<aside>{sidebar}</aside>
<main>{children}</main>
</div>
);
}
function App() {
return (
<Layout header={<strong>Site title</strong>} sidebar={<nav>Menu</nav>}>
<p>Main content</p>
</Layout>
);
}
How to know you are ready for the next set
You should be able to write each solution without looking, explain why the key is on the outer element in a list, and say why the Fragment was needed in exercise 6. If not, re-read the lessons linked in the hints. The next practice set covers state, events and hooks: Practice: State and Hooks.