Components and Props
Reviewed & published by Brayan K
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.
Part of the free React course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.
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
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 />
</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.
// 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!
// One definition, three results — that is the whole point of a component.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 — 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!
// Welcome, Linus!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} />The demo passes those same prop objects into a plain function so you can see destructuring and a default prop actually run:
// 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" }));
// [Alice | age 28 | Developer]
console.log(userCard({ name: "Bob", age: 34, role: "Designer" }));
// [Bob | age 34 | Designer]
console.log(userCard({ name: "Cleo", age: 22 })); // role left out
// [Cleo | age 22 | Member] ← the default kicked in
// Props are READ-ONLY: a component must never reassign its own props.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>
<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:
// 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>
// <li key="Cherry">Cherry</li>
// The 'key' is a unique id React uses to track each row efficiently.
// Without it React warns and list updates can glitch.// 🎯 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));
// ✅ Expected output:
// <li key="1">Sam</li>
// <li key="2">Mia</li>
// <li key="3">Tom</li>function Profile({ isLoggedIn, unread }) {
return (
<div>
{isLoggedIn ? <Dashboard /> : <LoginButton />}
{unread > 0 && <Badge count={unread} />}
</div>
);
}// 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 is false, React renders nothing (no badge).
function maybeBadge(count) {
return count > 0 && `🔔 ${count} new`; // false OR the string
}
console.log(maybeBadge(3)); // 🔔 3 new
console.log(maybeBadge(0)); // false → in React this renders NOTHING
// ⚠️ Gotcha: {count && <X/>} renders the number 0 when count is 0.
// Use a real boolean test instead: {count > 0 && <X/>}.// ❌ 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>
</>
);
}// === 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"; // ternary
return `${role} ${name} — ${activity}`;
}
// The "parent" maps the array, calling the child for each member.
team.map(member => memberLine(member)).forEach(line => console.log(line));
// 👑 Admin Alice — 42 posts
// 👤 User Bob — No posts yet
// 👤 User Cleo — 15 posts- 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 to Greeting and use <Greeting />.
- A stray 0 appears on screen: you wrote {count && <X/>}. When count is 0, that renders 0. 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 than props.title everywhere.
- 💡 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: 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: 1, name: "Mug", price: 8, onSale: false },
{ id: 2, name: "Hoodie", price: 35, onSale: true },
{ id: 3, name: "Sticker", price: 2, onSale: false },
];
// your code here🎉 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 children prop + nesting = composition
- ✅ Render lists with .map() and a unique key
- ✅ Choose UI with a ternary or show-if-true with &&
- ✅ Group elements without extra nodes using a Fragment <>...</>
Practice quiz
What must a React function component return to describe its UI?
- A string of HTML
- A DOM node created with document.createElement
- JSX (or null)
- An object with a render method
Answer: JSX (or null). A function component returns JSX (or null/strings/arrays). React turns that JSX into the actual DOM.
Why must a component's name start with a capital letter?
- React treats lowercase tags as built-in HTML elements
- It is only a style convention with no effect
- Lowercase names cause a syntax error
- It changes how props are passed
Answer: React treats lowercase tags as built-in HTML elements. React reads <greeting /> as the HTML tag 'greeting'. Capitalized names like <Greeting /> are treated as your components.
How does a parent pass data to a child component?
- Through global variables
- By mutating the child's state
- Through the children array only
- Through props (attributes on the JSX tag)
Answer: Through props (attributes on the JSX tag). Props are passed as attributes and arrive bundled as a single props object in the child.
What is true about props in React?
- A component may reassign its own props freely
- They are read-only inside the component that receives them
- They are always strings
- They can only be passed to class components
Answer: They are read-only inside the component that receives them. Props are read-only. A component must never reassign the props it receives; data flows down from the parent.
How do you pass a non-string value like a number as a prop?
- count={3}
- count="3"
- count=(3)
- count: 3
Answer: count={3}. Strings use quotes; any other JS value (numbers, booleans, objects) goes inside curly braces: count={3}.
Whatever you nest between a component's opening and closing tags arrives as which prop?
- content
- slot
- children
- inner
Answer: children. Nested content is passed as the special children prop, which the component renders with {children}.
When rendering a list with .map(), what does the key prop do?
- It sets the CSS class of each item
- It gives React a stable identity to track each item across renders
- It sorts the list
- It is required only for class components
Answer: It gives React a stable identity to track each item across renders. key is a stable unique id React uses to track each row efficiently when the list changes.
Which is the recommended value for a list item's key?
- The array index always
- Math.random()
- The item's text content
- A stable unique id from the data (e.g. item.id)
Answer: A stable unique id from the data (e.g. item.id). Use a stable, unique id. The array index is risky if items can reorder, insert, or delete.
What does {count && <Badge />} render when count is 0?
- Nothing
- A literal 0 on screen
- The Badge component
- An error
Answer: A literal 0 on screen. 0 is falsy, so && returns 0 and React renders the literal 0. Use {count > 0 && <Badge />} instead.
What lets a component return multiple sibling elements without adding an extra DOM node?
- A <div> wrapper
- An array literal
- A Fragment, written <>...</>
- The children prop
Answer: A Fragment, written <>...</>. A Fragment (<>...</> or <React.Fragment>) groups elements to satisfy the one-root rule without an extra node.
Continue this course
- Previous: JSX In Depth
- Next: State and Lifecycle — Make components dynamic by tracking changing data with state
- Quick reference: React cheat sheet › Components & Props