Props and composition
Make components reusable with props, understand why props are read-only, and build wrapper components with children. Your menu cards start showing real Brew & Bean data.
In this lesson you will make the menu card reusable by giving it inputs called props, and build layout components that wrap other content using children. By the end the page shows real Brew & Bean menu items from data.
Prerequisites: the project from *Components and JSX* (Header, MenuCard, Footer, App), and object destructuring from *Modern JavaScript for React*.
Props are function arguments
A component is a function, and functions take arguments. In React the arguments arrive as one object called props. Whatever you write as attributes on the tag becomes a property of that object.
Analogy: a form template at a bank. The template (component) is the same for everyone. The details filled in (props) differ for each customer.
function MenuCard(props) {
return <h3>{props.name}</h3>;
}
// used as: <MenuCard name="Espresso" />
// props is: { name: "Espresso" }The attribute name="Espresso" becomes props.name.
Almost everyone destructures props in the parameter list, so you write name instead of props.name:
function MenuCard({ name, price }) {
return <h3>{name}: ₹{price}</h3>;
}Same thing, shorter. The braces in the parameter list are object destructuring, not JSX.
Passing values: strings versus everything else
Quotes versus braces
| You write | The prop value is |
|---|---|
| name="Espresso" | The string Espresso (quotes mean a string) |
| price={120} | The number 120 (braces mean JavaScript) |
| soldOut={true} or just soldOut | The boolean true |
| tags={["vegan", "new"]} | An array |
| item={{ id: "espresso", price: 120 }} | An object (double braces: braces plus object) |
| onAdd={handleAdd} | A function |
The classic mistake
price="120" passes the string "120", not the number 120. Strings do not add like numbers ("120" + 60 gives "12060"). Use braces for anything that is not text.
Upgrade MenuCard
What to do
- 1Open
src/MenuCard.jsxand replace it with the version below. - 2Open
src/App.jsxand replace it with the version below, which passes different props to each card. - 3Append the extra CSS below to
src/index.css. - 4Check the browser: three different cards, one marked sold out.
export default function MenuCard({ name, price, category, soldOut = false }) {
return (
<article className={soldOut ? "card sold-out" : "card"}>
<h3>{name}</h3>
<p className="category">{category}</p>
<p className="price">₹{price}</p>
{soldOut && <p className="badge">Sold out</p>}
</article>
);
}soldOut = false is a default value used when the prop is not passed. The className uses a ternary to choose between two class names. soldOut && <p>...</p> shows the badge only when soldOut is true.
import Header from "./Header.jsx";
import MenuCard from "./MenuCard.jsx";
import Footer from "./Footer.jsx";
export default function App() {
return (
<>
<Header />
<main className="page">
<h2>Today's menu</h2>
<div className="menu-grid">
<MenuCard name="Espresso" price={120} category="coffee" />
<MenuCard name="Masala Chai" price={90} category="tea" />
<MenuCard name="Banana Bread" price={130} category="bakery" soldOut />
</div>
</main>
<Footer />
</>
);
}Writing soldOut with no value is shorthand for soldOut={true}.
.sold-out {
opacity: 0.6;
}
.badge {
display: inline-block;
margin: 8px 0 0;
padding: 2px 8px;
background: #b3261e;
color: #fff;
border-radius: 999px;
font-size: 0.8rem;
}Three cards: Espresso ₹120, Masala Chai ₹90 and Banana Bread ₹130 with a faded look and a red "Sold out" badge.Props flow down and are read-only
Data moves in one direction: a parent passes props to its child. The child must never change its props. If it needs to remember or change something, that is state, covered in the next lesson.
Why? If any component could change its inputs, you could not tell where a value came from. One-way flow makes a React app predictable: to find out why a card shows "₹120", look at who rendered it.
Props are read-only
Do not write price = price + 10 inside a component. Calculate a new variable instead: const discounted = price - 10;. Assigning to a prop is a bug even when it seems to work.
Children and composition
Sometimes a component should wrap other content, like a section box with a title that holds anything inside. Whatever you put between the opening and closing tags arrives as the special prop children.
This idea is called composition: build bigger pieces by nesting components, instead of making one giant component with dozens of options.
export default function Section({ title, children }) {
return (
<section className="section">
<h2>{title}</h2>
{children}
</section>
);
}Section knows nothing about what goes inside. It adds a title and a wrapper, and renders children where you place it.
import Header from "./Header.jsx";
import Section from "./Section.jsx";
import MenuCard from "./MenuCard.jsx";
import Footer from "./Footer.jsx";
export default function App() {
return (
<>
<Header />
<main className="page">
<Section title="Coffee">
<div className="menu-grid">
<MenuCard name="Espresso" price={120} category="coffee" />
<MenuCard name="Cold Brew" price={220} category="coffee" />
</div>
</Section>
<Section title="Bakery">
<div className="menu-grid">
<MenuCard name="Butter Croissant" price={150} category="bakery" />
<MenuCard name="Banana Bread" price={130} category="bakery" soldOut />
</div>
</Section>
</main>
<Footer />
</>
);
}Each Section wraps its own grid of cards. Section is reused with different titles and different children.
.section {
margin-bottom: 32px;
}Typing every card by hand is tedious. In the *Lists, keys and conditional rendering* lesson you will loop over an array of menu items instead. Until then, notice the shape: each card needs name, price and category, which is exactly what one menu item object holds.
Spreading an object into props
const item = { name: "Cappuccino", price: 180, category: "coffee" };
// these two lines are the same:
<MenuCard name={item.name} price={item.price} category={item.category} />
<MenuCard {...item} />The spread syntax {...item} turns each property into a prop. Useful, but make sure the property names match the prop names.
Naming and size
Keep components small and give props clear names: price, not p; soldOut, not flag. If a component has more than five or six props, it is often doing too much. Consider splitting it or using children.
Here is a React component (plain JavaScript, hooks only, no classes):
---
[paste your component]
---
Review its props: are the names clear, are there props it doesn't need, are defaults sensible, and would `children` make it more flexible? Suggest a better version and explain each change in one sentence.What to expect: Look for concrete changes you can explain back. Ignore advice that adds libraries or complexity you do not need yet.
Try it: Build a Callout component
Create src/Callout.jsx that takes a type prop ("info" or "warning", default "info") and children, and renders a coloured box. Use it in App.jsx twice: an info callout saying "Pickup orders only" and a warning callout saying "Banana Bread is sold out today".
Check yourself
- I can say what props is (one object of inputs) and destructure it
- I know when to use braces versus quotes for a prop value
- My components never change their own props
- I can wrap content with a component using
children - I gave optional props a default value
Key takeaways
- Props are the inputs to a component, received as one object; destructure them in the parameter list.
- Quotes pass strings; braces pass numbers, booleans, arrays, objects and functions.
- Data flows down from parent to child and props are read-only.
childrenlets a component wrap other content; composition beats giant components with many options.- Use default values for optional props and
{...object}to pass many properties at once.
Check yourself
Finishing the quiz marks the lesson as done.
1. What does <MenuCard name="Espresso" price={120} /> give the MenuCard function?
2. A learner writes <MenuCard price="120" /> and later adds price + 60. What do they get?
3. A child component needs to change a value it received as a prop. What is the right approach?
4. Where does the content between <Section title="Coffee"> and </Section> end up inside Section?
5. What does soldOut written alone, as in <MenuCard soldOut />, mean?
Saved on this device. Create a free account to keep it on every device.
Practise this with Sam
A real mock interview, out loud, with an honest scorecard. Free to start.