Skip to content
wannacode

Props: data from above

In short: Props are the data a component gets from its parent, the way a function gets parameters; the child does not change them, and children passes in content from outside.

Problem

Bread three times

The catalog must show three products: bread, milk and cheese. The product card is ready from the last lesson, so App puts it in three times.

React

All three cards are bread. The product is written inside ProductCard, and every card shows it. The other way is BreadCard, MilkCard and CheeseCard: three copies of the same component, and every change to the card has to be made three times. What is missing is a way to tell the card which product to show.

The idea

Props: data from above

A component is a function. A function gets its data as parameters, and a component gets them as attributes from its parent: the component that uses it. These attributes are called props (from properties). The parent writes them, the child reads them.

App.jsx
<ProductCard product={bread} />
<ProductCard product={milk} />
ProductCard.jsx
export default function ProductCard({ product }) {
return <h2>{product.name}</h2>;
}

React collects all attributes into one object and passes it to the function: { product: bread }. The notation ({ product }) takes the property product out of it (destructuring). Without the braces the parameter is the whole object: function ProductCard(props), and then props.product.name.

There is one card, and every use has different data. A function works the same way: one definition, many calls with different arguments.

See it

See how they flow

const bread = { name: 'White bread', price: 1.2 };
const milk = { name: 'Fresh milk', price: 1.6 };
const cheese = { name: 'White cheese', price: 7.9 };
function ProductCard({ product, discount = 0 }) {
return (
<article>
<h2>{product.name}</h2>
<p>Price: {money(product.price * (1 - discount / 100))}</p>
</article>
);
}
export default function App() {
return (
<main>
<h1>Bread and milk</h1>
<div>
<ProductCard product={bread} />
<ProductCard product={milk} />
<ProductCard product={cheese} discount={10} />
</div>
</main>
);
}
Step by step, in words
  1. App holds the data: three products. Below it are three ProductCards: the same component three times, and none of them knows yet what to show.
  2. product={bread} hands the object bread to the first card. The attributes of a component are called props.
  3. The other two cards get their own product. The third one also gets discount = 10. Each card has its own props.
  4. Each card draws itself from its props. Bread and milk got no discount, so it is 0 by default. The cheese is 10% off: €7.11.
  5. If the card tries props.discount = 0, nothing goes up: in development React throws a TypeError (the props object is frozen), and App’s data stay the same.
  6. Data flow one way only: from the parent down to the child. To change something above, the child has to ask the parent. That is the module "State".

Every card knows only its own props. It does not see the other cards, and it does not know where the data in App come from.

See it

One card, three products

React

The products are in App, and there is one ProductCard. Add a fourth product in App (the image img/apples.svg exists) and one more <ProductCard … />. You do not touch the card. With 30 products you will want a list rather than 30 lines: that is the next lesson.

Try it

Find the bug

The card does not show, and the console says "TypeError: Cannot read properties of undefined (reading 'toFixed')".

Pick the line with the bug.

The idea

A default value

Some products are on sale. The card gets a second prop, discount, in percent. Most products have no discount, and it is not nice for every line in App to write discount={0}. A missing prop is undefined, like a missing function argument. The price becomes price * (1 - undefined / 100), which is NaN.

So the prop gets a default value:

export default function ProductCard({ product, discount = 0 }) {

This is a default value in JavaScript destructuring: if discount is missing, it is 0. In older code you will see ProductCard.defaultProps = { discount: 0 } and propTypes. React 19 no longer looks at them for function components: checking types is the job of TypeScript.

Try it

A discount

App gives the cheese a discount of 10 (percent). Make the card take discount, 0 by default, and show the price with the discount: the bread €1.20, the milk €1.60, the cheese €7.11.

React

The idea

Read-only

The data belong to whoever holds them. The card can read its props, but it cannot change them. Try the "One more" button: the child tries to increase its own qty.

React

The quantity stays 2, and the console says "TypeError: Cannot assign to read only property 'qty' of object". In development mode React freezes the props object, exactly to catch code like this.

Even if it went through, the change would be only in the child: App would still say 2, and on the next drawing App would pass 2 again. Two places with different truths are the bug from "A list from data". The rule: data flow down, and changing them is the parent’s job. How a child asks its parent to change something is the module "State".

The idea

Content from outside

The shop has frames that look the same with different things inside: "Delivery" with two paragraphs, "Returns" with one. The title is a prop. The content is a whole piece of HTML, and it is handier to write it between the opening and the closing tag, as with <section>.

<Card title="Delivery">
<p>Within 2 working days.</p>
</Card>
export default function Card({ title, children }) {
return (
<section className="card">
<h2>{title}</h2>
{children}
</section>
);
}

Everything between <Card> and </Card> arrives in the special prop children. Card decides where to put it.

React

Try it

The frame

Card shows only the title. Make it also show the content that App writes between <Card> and </Card>, under the title.

React
More precisely, if you are curious

More than one place for content. There is one children. If the frame also has a bottom part, it is one more prop with JSX: <Card title="Delivery" footer={<a href="/delivery">Details</a>}>…</Card>, and Card puts {footer} where it belongs.

All properties at once. <ProductCard {...product} /> passes every property of the object as a separate prop: name, price, image. It is handy, but you cannot see what exactly the card gets.

Only the first level is frozen. props.qty = 3 throws an error, but props.product.price = 2 goes through silently: the product object is not frozen, it is the same object as in App. That way the child changes the parent’s data behind its back. Do not do it.

Types. React 19 no longer checks propTypes of function components and does not use defaultProps for them. The types of props are written with TypeScript: it is in the lesson on a real project.

Exercise

The shop window

Build the shop window from scratch. ProductCard takes product and discount (0 by default) and shows the image (alt is the name), the name as a level-two heading and "Price: …" with the discount. Card takes title and shows the title (level two) and the content from outside. In App, under the heading: the three cards in a div (the cheese with discount 10), then a Card "Delivery" with "Within 2 working days." and "Free over €30.".

React

Test

Check yourself

1. What are props?
2. What does the card get from <ProductCard product="bread" />?
3. The card gets qty and wants to add 1 to it. Which is true?
4. App writes <Card title="Delivery"><p>Within 2 working days.</p></Card>. How does Card show the paragraph?
5. App writes <ProductCard product={bread} /> without discount. What is discount in the card?

Made by Ivo Gergov as part of Evo Tech's campaign for accessible programming education ·