Lesson 2 • Beginner
Components and Props ⚛️
By the end of this lesson you'll be able to build reusable function components, pass data into them with props, nest them to compose a screen, render lists with .map(), and show or hide content conditionally — the core skills behind every React UI.
What You'll Learn
- Write a function component that returns JSX
- Pass data down with props and destructure them cleanly
- Give props default values and use the children prop
- Compose UIs by nesting small components inside bigger ones
- Render a list with .map() and a correct unique key
- Show or hide content with && and the ternary operator
npm create vite@latest my-app -- --template react.1️⃣ Function Components
A React component is simply a JavaScript function that returns JSX (the HTML-like markup describing what to show). “Rendering” a component just means React calls that function and uses what it returns. Define it once, reuse it anywhere. One non-negotiable rule: a component name must start with a capital letter (PascalCase) — React reads lowercase tags as plain HTML, so <greeting /> would silently do nothing.
Here's a complete component. Read it first — every part has a job:
// A function component — note the Capital G.
function Greeting() {
return <h1>Hello, World!</h1>; // returns JSX (its UI)
}
// You "use" it like an HTML tag, and you can reuse it freely:
function App() {
return (
<div>
<Greeting />
<Greeting /> {/* same definition, second instance */}
</div>
);
}The demo below runs the same idea as plain JavaScript so you can watch a function return its output and be reused — exactly what React does when it renders a component.
Worked example: a component is a function that returns UI
Run it and watch one definition produce three results.
// A React component is just a function that RETURNS its UI.
// Here we run the same idea as plain JavaScript so you can see the output.
function greeting() {
return "Hello, World!"; // In React this 'return' produces <h1>Hello, World!</h1>
}
// "Rendering" a component = calling its function and using what comes back.
console.log(greeting()); // Hello, World!
console.log(greeting()); // Hello, World! (reuse it as many times as you like)
console.log(greeting()); // Hello, World!
//
...Now your turn. A component returns its output, so build a function that returns a string (don't console.log inside it). Fill in the blank:
🎯 Your turn: return your UI
Make welcome(name) return a greeting string. Check it against the expected output.
// 🎯 YOUR TURN — fill in each ___ then press "Try it Yourself".
// A component is a function that returns its output. Build that function.
// 1) Make a function called "welcome" that takes a name
function welcome(name) {
// 2) Return a greeting string that includes the name.
// Use a template literal with backticks: `...${name}...`
return ___; // 👉 e.g. `Welcome, ${name}!`
}
console.log(welcome("Ada"));
console.log(welcome("Linus"));
// ✅ Expected output:
// Welcome, Ada!
...2️⃣ Props — Passing Data Down
Props (short for “properties”) are how a parent component hands data to a child. They behave just like function arguments, and they all arrive bundled into a single object called props. Two habits make them pleasant to work with: destructuring (pull the fields you need straight out in the parameter list) and default values (a fallback used when a prop isn't passed).
Critically, props are read-only. A component must never change its own props — that's data flowing down from the parent, and rewriting it breaks React's mental model. (When data needs to change, you'll use state — the next lesson.)
// Destructure name, age, role from props. role defaults to "Member".
function UserCard({ name, age, role = "Member" }) {
return (
<div className="card">
<h2>{name}</h2>
<p>Age: {age}</p>
<p>Role: {role}</p>
</div>
);
}
// The parent passes data as attributes. Strings use "quotes";
// any other value (numbers, booleans) goes inside { curly braces }.
<UserCard name="Alice" age={28} role="Developer" />
<UserCard name="Cleo" age={22} /> {/* role falls back to "Member" */}The demo passes those same prop objects into a plain function so you can see destructuring and a default prop actually run:
Worked example: props, destructuring & defaults
See how a missing prop falls back to its default value.
// PROPS = the data a parent passes into a child. They arrive as one object.
// In React you'd write <UserCard name="Alice" age={28} />.
// That object is exactly what we pass to the function below.
// Destructuring pulls fields straight out of the props object.
// 'role = "Member"' is a DEFAULT — used only when role isn't passed.
function userCard({ name, age, role = "Member" }) {
return `[${name} | age ${age} | ${role}]`;
}
console.log(userCard({ name: "Alice", age: 28, role: "Developer" }
...3️⃣ Children & Composition
Everything you put between a component's opening and closing tags arrives as a special prop called children. That lets you build generic wrappers — a Card, a Modal — that don't care what they contain. This is composition: building complex UIs by nesting small, focused components, which React prefers over inheritance.
function Card({ children }) {
return <div className="card">{children}</div>; // render whatever was passed in
}
// Compose a whole page by nesting components inside each other:
function App() {
return (
<Page>
<Header>
<Logo />
<NavMenu />
</Header>
<Card>
<h2>Welcome back!</h2> {/* this becomes Card's children */}
<p>Glad to see you.</p>
</Card>
</Page>
);
}Composition keeps each component small and reusable: Card handles the border and padding once, and every screen reuses it with different children.
4️⃣ Rendering Lists with .map()
UIs are full of repeated rows — a to-do list, search results, a menu. You don't write each one by hand; you take an array and transform it into UI with .map(), which returns a new array containing one element per item. Each element needs a key: a stable, unique id React uses to track rows when the list changes. Use a real id from your data — never the array index if items can be reordered, added, or removed.
function FruitList() {
const fruits = ["Apple", "Banana", "Cherry"];
return (
<ul>
{fruits.map(fruit => (
<li key={fruit}>{fruit}</li> // key = unique id for this row
))}
</ul>
);
}The demo runs the .map() behind that JSX so you can see exactly what it builds — one line per item:
Worked example: map an array into list items
Watch .map() turn 3 fruits into 3 <li> rows, each with a key.
// RENDERING A LIST = take an array, turn each item into a piece of UI.
// The tool is Array.map(): it returns a NEW array, one result per item.
const fruits = ["Apple", "Banana", "Cherry"];
// In React: {fruits.map(f => <li key={f}>{f}</li>)}
// Here we build the matching <li> strings so you can see what map produces:
const items = fruits.map(fruit => `<li key="${fruit}">${fruit}</li>`);
items.forEach(line => console.log(line));
// <li key="Apple">Apple</li>
// <li key="Banana">Banana</li>
...Your turn — this is the single most common React pattern. Map an array of objects, using each object's id as the key and its name as the text. Fill in the two blanks:
🎯 Your turn: map objects with keys
Use user.id for the key and user.name for the text. Match the expected output.
// 🎯 YOUR TURN — fill in each ___ then run it.
// Turn this array of users into one <li> string per user.
const users = [
{ id: 1, name: "Sam" },
{ id: 2, name: "Mia" },
{ id: 3, name: "Tom" },
];
// 1) Use .map() to build a string for each user.
// 2) Each string should use user.id as the key and show user.name.
const rows = users.map(user => `<li key="${___}">${___}</li>`);
// 👆 user.id 👆 user.name
rows.forEach(line => console.log(line
...5️⃣ Conditional Rendering
Because JSX is just an expression, you choose what to show with normal JavaScript. The two everyday tools: the ternary condition ? a : b when you're picking between two things, and the && operator when you want to show something only if a condition is true (and render nothing otherwise).
function Profile({ isLoggedIn, unread }) {
return (
<div>
{/* ternary: pick one of two */}
{isLoggedIn ? <Dashboard /> : <LoginButton />}
{/* && : show only when true */}
{unread > 0 && <Badge count={unread} />}
</div>
);
}One trap with &&: write {unread > 0 && ...}, not {unread && ...}. If unread is 0, the second form renders a literal 0 on screen. The demo shows both operators running:
Worked example: ternary and && rendering
See how && returns false (renders nothing) when the condition fails.
// CONDITIONAL RENDERING = decide what UI to show based on a condition.
// 1) Ternary (condition ? whenTrue : whenFalse) — pick ONE of two things.
function statusLabel(isOnline) {
return isOnline ? "🟢 Online" : "⚫ Offline"; // In JSX: {isOnline ? ... : ...}
}
console.log(statusLabel(true)); // 🟢 Online
console.log(statusLabel(false)); // ⚫ Offline
// 2) && (logical AND) — show something ONLY when the condition is true.
// In JSX: {count > 0 && <Badge />}
// If the left side
...6️⃣ Fragments — Returning Multiple Elements
A component can only return one root element. Try to return two siblings side by side and you'll get an error. The old fix was to wrap them in a <div>, but that adds clutter to your HTML. A Fragment groups elements without adding an extra node — write it as <>...</> (the short form) or <React.Fragment>.
// ❌ Error: two root elements, nothing wrapping them
function Bad() {
return (
<h1>Title</h1>
<p>Subtitle</p>
);
}
// ✅ Fragment groups them — no extra <div> in the output
function Good() {
return (
<>
<h1>Title</h1>
<p>Subtitle</p>
</>
);
}Putting It Together: a Team List
This combines everything: a small child “component” renders one member, a parent maps the array to call it for each row, and each line uses ternaries to pick a role and an activity label. In real React, memberLine would be a <MemberLine /> component with a key.
Worked example: composition + map + conditionals
Change a member's posts or isAdmin and rerun to see the labels update.
// === A small "team list" — composition + list + conditionals together ===
const team = [
{ id: 1, name: "Alice", isAdmin: true, posts: 42 },
{ id: 2, name: "Bob", isAdmin: false, posts: 0 },
{ id: 3, name: "Cleo", isAdmin: false, posts: 15 },
];
// One small "component" (function) renders ONE member.
function memberLine({ name, isAdmin, posts }) {
const role = isAdmin ? "👑 Admin" : "👤 User"; // ternary
const activity = posts > 0 ? `${posts} posts` : "No posts yet";
...Common Errors (and the fix)
- “Warning: Each child in a list should have a unique ‘key’ prop.” — You mapped an array without a
key. Add<li key={item.id}>using a stable unique id, not the array index. - Mutating props:
props.name = "New"is a bug — props are read-only data owned by the parent. Pass new props down, or use state for values that change. - “Adjacent JSX elements must be wrapped in an enclosing tag.” — You returned two root elements. Wrap them in a Fragment
<>...</>(or a single parent element). - Component renders as plain text / does nothing: you named it lowercase.
<greeting />is treated as HTML — rename toGreetingand use<Greeting />. - A stray
0appears on screen: you wrote{count && <X/>}. Whencountis 0, that renders0. Use a boolean:{count > 0 && <X/>}.
Pro Tips
- 💡 One job per component. If a component grows past ~100 lines or does several unrelated things, split it up.
- 💡 Destructure props in the parameter list —
function Card({ title }) {}reads better thanprops.titleeverywhere. - 💡 Keys belong on the element
.map()returns, not on its inner children. - 💡 Data flows down. Parents pass props to children; children never reach back up and change them.
📋 Quick Reference
| Pattern | Syntax |
|---|---|
| Component | function Name() { return <div />; } |
| Use it | <Name /> |
| Props + destructure | function Card({ title, children }) {} |
| Default prop | { color = "blue" } |
| Pass a number/bool | <Card count={3} open={true} /> |
| List | {items.map(i => <li key={i.id}>{i.name}</li>)} |
| Ternary | {ok ? <Yes /> : <No />} |
| Show if true | {count > 0 && <Badge />} |
| Fragment | <><h1/><p/></> |
Frequently Asked Questions
Q: Why must component names start with a capital letter?
React decides what a tag means by its case. Lowercase tags like <div> are treated as built-in HTML; capitalised tags like <Greeting> are treated as your components. Lowercase your component and React looks for an HTML element of that name and renders nothing useful.
Q: What exactly is the key for, and can I use the array index?
The key gives React a stable identity for each list item so it can update the minimum needed when the list changes. A real, unique id (e.g. item.id) is best. The array index works only if the list never reorders, inserts, or deletes — otherwise it causes subtle bugs.
Q: Props vs state — what's the difference?
Props are passed in from a parent and are read-only to the child. State is data a component owns and can change over time. This lesson is all props; state is next.
Q: When should I reach for a Fragment instead of a <div>?
Use a Fragment <>...</> whenever you only need to group elements to satisfy the “one root” rule and an extra <div> would mess up your layout or semantics (e.g. inside a table row or a flex container).
Mini-Challenge: Product List
No blanks this time — just a brief and a starter array. Map the products into display lines and add a sale tag conditionally. Run it and check your output against the example in the comments. This is the exact pattern real product pages are built from.
🎯 Mini-Challenge: build a product list
Map the array, format each line, and add (SALE!) only when onSale is true.
// 🎯 MINI-CHALLENGE: Product list
// You have an array of products. Build one display line per product.
//
// 1. Map over 'products' to create one string per item.
// 2. Each line should show the name and price, e.g. "Mug — £8".
// 3. If a product is on sale (onSale === true), add " (SALE!)" to the line.
// Hint: use && or a ternary for the sale tag.
// 4. console.log every line.
//
// ✅ Expected output:
// Mug — £8
// Hoodie — £35 (SALE!)
// Sticker — £2
const products = [
{ id:
...🎉 Lesson Complete
- ✅ A component is a PascalCase function that returns JSX
- ✅ Props pass data down; destructure them and give defaults; they're read-only
- ✅ The
childrenprop + nesting = composition - ✅ Render lists with
.map()and a uniquekey - ✅ Choose UI with a ternary or show-if-true with
&& - ✅ Group elements without extra nodes using a Fragment
<>...</> - ✅ Next lesson: State and Lifecycle — make components remember and react to change
Sign up for free to track which lessons you've completed and get learning reminders.