cf.completefrontend
Open lab My progress
THE REACT FIELD GUIDE

Passing props to a component

Pass props to React components and read them with destructuring, defaults, and children. See why props are read-only, and how they change over time.

By the end, you can
  • 01
    Pass and read propsGive a component props from its parent, read them with destructuring, set defaults for missing ones, and pass markup as children.
  • 02
    Forward props safelyPass an object's properties with spread, forward the rest to an element, and avoid the stray attributes and overwritten values spread can cause.
  • 03
    Treat props as read-only snapshotsExplain what React 19 does when a component writes to its props, in development and in production, and how a parent passes new props instead.

One card component, many teas

In Your first component, the menu page used TeaCard three times, and all three cards said the same thing, because the tea was typed into the component. A real menu needs each card to show its own tea. The parent, MenuPage, should say which tea each card shows, and TeaCard should only draw it. That is what props are for. The result is live:

A menu where each card gets its own teaPop out in the code editor (opens in a new tab)JSX
function TeaCard({ name, price }) {  return (    <article className="tea-card">      <h3>{name}</h3>      <p>${price}</p>    </article>  );} export default function MenuPage() {  return (    <main>      <TeaCard name="Green tea" price={3} />      <TeaCard name="Chai" price={4} />      <TeaCard name="Iced lemon tea" price={5} />    </main>  );}
ResultLoading…

Look at the bottom first. Lines 13 to 15 use TeaCard three times, each with two attributes, like name="Chai" price={4} on line 14. On a component's tag, these are called props, short for "properties". They're written like the attributes in JavaScript in JSX with curly braces: a string in quotes, anything else in braces.

Now the top. Line 1 says TeaCard takes a name and a price, and lines 4 and 5 show them. The result shows three different cards from one function: Green tea $3, Chai $4, and Iced lemon tea $5. Press Edit & run and change price={4} on line 14 to price={6}: only the Chai card changes.

Three cards with arrows between them. The attributes name="Chai" price={4} on a TeaCard tag become one object, name Chai and price 4, which goes to the function TeaCard.name="Chai" price={4}on the tagin the parent{ name: "Chai", price: 4 }one props objectTeaCard()the componentreads it
The parent writes props on the tag: <TeaCard name="Chai" price={4} />. React gathers them into one object and calls TeaCard with it, the way you pass an argument to a function.
Real-life analogyAn order form at a print shop

A print shop has one business card design. Each customer fills in an order form with their own name and phone number, and the printer prints from the form. The printer never edits your form; if you want a different card, you hand in a new one.

In real life: One business card design the shop keeps
In React: The TeaCard component
In real life: The order form you fill in: name, phone number
In React: The props on the tag: name, price
In real life: The printer reads your form, but never writes on it
In React: The component reads its props, but never changes them
In real life: For a different card, you hand in a new form
In React: For a different card, the parent passes new props
The main idea

Props are the values a parent passes to a component. The parent writes them on the component's tag. React gathers them into one object and calls the component with it, like an argument to a function. The component reads its props to decide what to show, and never changes them.

Props arrive as one object

A component is a function, and it gets all its props in its first parameter, as one object. This card takes that object whole, calls it props, and prints it. Press Edit & run to see the console:

The whole props objectPop out in the code editor (opens in a new tab)JSX
function TeaCard(props) {  console.log(props);  return (    <article className="tea-card">      <h3>{props.name}</h3>      <p>${props.price}</p>    </article>  );} export default function MenuPage() {  return <TeaCard name="Green tea" price={3} />;}
ResultLoading…

Line 1 names the parameter props. Line 2 prints it: { name: "Green tea", price: 3 }, the two attributes from line 12 as an object. Lines 5 and 6 read each one with a dot, props.name and props.price. You'll see the log twice: in development, the StrictMode every new project has calls each component twice to catch mistakes, which the Keeping components pure lesson explains.

Where does the object come from? Remember from Writing markup with JSX that every tag becomes a jsx() call. Here is line 12, compiled:

Line 12, compiledJavaScript
jsx(TeaCard, { name: "Green tea", price: 3 });

The first argument is your function, TeaCard, and the second is the props object, built from the attributes. When React renders this element, it calls TeaCard with that object. Props are a function's arguments, gathered into one object.

Most components don't keep the whole object. They take out the props they need with destructuring, right in the parameter list, as the menu's line 1 does: function TeaCard({ name, price }). That's short for this:

What { name, price } in the parameters stands forJavaScript
function TeaCard(props) {
  const { name, price } = props;
  // …the same return as before
}

Line 2 makes two variables from the object's properties of the same names. A prop you don't name is simply not used. Destructuring is the usual way to read props, and the JavaScript you need for React lesson shows more of it.

Since props are function arguments, they can be any JavaScript value. The quotes or braces you write decide which. These two tags look almost the same:

A number in braces, a string in quotesPop out in the code editor (opens in a new tab)JSX
function PriceTag({ price }) {  return <p>With a cookie: ${price + 1}</p>;} export default function App() {  return (    <>      <PriceTag price={3} />      <PriceTag price="3" />    </>  );}
ResultLoading…

Line 8 passes the number 3, so line 2 adds 1 and shows $4. Line 9 passes the string "3", and + on a string joins text, so it shows $31. Use braces for numbers, booleans, and everything that isn't text. Objects and functions work too:

An object and a function as propsPop out in the code editor (opens in a new tab)JSX
function TeaCard({ tea, onAdd }) {  return (    <article className="tea-card">      <h3>{tea.name}</h3>      <p>${tea.price}</p>      <button onClick={onAdd}>Add to cart</button>    </article>  );} export default function MenuPage() {  return (    <TeaCard      tea={{ name: "Chai", price: 4 }}      onAdd={() => console.log("Chai added")}    />  );}
ResultLoading…

Line 14 passes an object, with the double curlies from the last lesson: outer braces for JavaScript, inner ones for the object. Line 15 passes an arrow function, and line 6 hands it to the button's onClick. Click Add to cart: the console says Chai added. Passing functions is how a child tells its parent something happened; the Responding to events lesson goes further.

Two names are special. key, which you'll give list items, is kept by React and never reaches your component; props.key is undefined, and React warns if you read it. And since React 19, ref reaches a function component as an ordinary prop; Manipulating the DOM with refs covers it.

Default values for missing props

Most teas come in a regular size, so the parent shouldn't have to write size="Regular" every time. Destructuring can give a prop a default value, with =:

function TeaCard({ name, price, size = "Regular" }) {  return (    <article className="tea-card">      <h3>{name}</h3>      <p>{size}: ${price}</p>    </article>  );} export default function MenuPage() {  return (    <main>      <TeaCard name="Green tea" price={3} />      <TeaCard name="Chai" price={5} size="Large" />      <TeaCard name="Oolong" price={4} size={undefined} />      <TeaCard name="Mint tea" price={3} size={null} />    </main>  );}
ResultLoading…

Line 1 says: if size is missing, use "Regular". The four cards show when that happens:

  • Line 13 leaves size out. It's undefined, so the default fills in: Regular: $3.
  • Line 14 passes "Large", so the default isn't needed: Large: $5.
  • Line 15 passes undefined on purpose. That counts as missing: Regular: $4.
  • Line 16 passes null. That's a value, so the default isn't used, and React shows nothing for null: the card says only : $3.

How to read this table: each row is one way a parent can write the prop, and the value size gets.

When a default is used
When the parent writessize isWhy
<TeaCard name="Green tea" price={3} />"Regular", the default.The prop is missing, so size is undefined and the default fills in.
size="Large""Large".The parent passed a value, so the default isn't used.
size={undefined}"Regular", the default.Passing undefined is the same as leaving the prop out.
size={null}null, and nothing shows.null is a value, so the default isn't used. React shows nothing for it.
size="" or size={0}"" or 0.Empty text and zero are values too. Only undefined gets the default.

Older React code set defaults another way, with a defaultProps object on the function. The React 19 upgrade guide removed it from function components, and React 19.3.1 ignores it without a word:

defaultProps on a function component (ignored in React 19)Pop out in the code editor (opens in a new tab)JSX
function TeaCard({ name, size }) {  return <p>{name}: [{size}]</p>;} TeaCard.defaultProps = { size: "Regular" }; export default function MenuPage() {  return <TeaCard name="Green tea" />;}
ResultLoading…

Line 5 sets the old-style default, and line 8 leaves size out. The brackets on line 2 stay empty: Green tea: []. If you meet defaultProps in older code, move each default into the destructuring. (Class components, an older way to write components, still support it.)

The children prop: markup inside a component

So far every component tag closed itself, <TeaCard />. A component can also have an opening and a closing tag, with markup between them, like a <div>. That markup reaches the component as a prop named children. This is how you build wrappers: a panel, a card frame, a dialog box.

A Panel that wraps any markupPop out in the code editor (opens in a new tab)JSX
function Panel({ title, children }) {  return (    <section className="panel">      <h3>{title}</h3>      {children}    </section>  );} export default function ShopPage() {  return (    <>      <Panel title="Today's special">        <p>Chai with a free cookie, $4.</p>      </Panel>      <Panel title="Join the tea club">        <input placeholder="Email" />        <button>Join</button>      </Panel>    </>  );}
ResultLoading…

Line 1 takes two props, title and children. Line 4 shows the title, and line 5 places {children} under it. In ShopPage, lines 13 to 15 put a paragraph between <Panel> and </Panel>, and lines 16 to 19 put a box and a button. The result shows both panels, each with its own content, inside the same frame from styles.css.

children isn't special syntax. Here are lines 13 to 15 as TypeScript's compiler writes them:

Lines 13 to 15, compiledJavaScript
jsx(Panel, {
  title: "Today's special",
  children: jsx("p", { children: "Chai with a free cookie, $4." }),
});

The paragraph is just another key in the props object, on line 3. You could even write children= as an attribute; nobody does, because markup between the tags is easier to read.

Left: a card with a p tag, from between the Panel tags in ShopPage. An arrow leads into a crate labelled Panel, which holds a card with h3 and title in braces, and below it a shaded card with children in braces, where the paragraph goes.<p>…</p>between thePanel tags<h3>{title}</h3>{children}Panel
Whatever ShopPage writes between <Panel> and </Panel> arrives in the children prop. Panel decides where it goes by writing {children} in its own markup.

What arrives in children depends on what you write between the tags. Pick one below, and React renders it on this page:

Playground: what arrives in children
What goes between the tags, on line 6JSX
function Inspect({ children }) {  return <div className="inspect">{children}</div>;} export default function App() {  return <Inspect></Inspect>;}
The pageInspect

children is undefined

Try it yourself

Nothing between the tags, so the component gets no children prop: it's undefined.

React renders Inspect on this page for real with what you picked between its tags, and Inspect describes the children prop it received.

Nothing between the tags gives undefined, text gives a string, one tag gives one element, and two or more pieces give an array. Even Chai: {4} is two pieces, the text and the number. You rarely need to know which: place {children} in your markup, and React shows whatever it is, as the last lesson's braces do.

Passing props along with spread

Tea data often arrives as objects, and writing name={chai.name} price={chai.price} … for every property is long. JSX lets you spread an object onto a tag:

Every property of an object as a propPop out in the code editor (opens in a new tab)JSX
const chai = { name: "Chai", price: 4, size: "Large" }; function TeaCard({ name, price, size }) {  return (    <article className="tea-card">      <h3>{name}</h3>      <p>{size}: ${price}</p>    </article>  );} export default function MenuPage() {  return <TeaCard {...chai} />;}
ResultLoading…

Line 13, <TeaCard {...chai} />, passes each property of chai (line 1) as its own prop: the same as name={chai.name} price={chai.price} size={chai.size}. The card shows Large: $4.

Spread also works the other way, to pass along props you don't use. This is common for small components that wrap one HTML element:

A button that forwards the rest of its propsPop out in the code editor (opens in a new tab)JSX
function ShopButton({ variant = "plain", ...rest }) {  return <button className={`btn ${variant}`} {...rest} />;} export default function Checkout() {  return (    <>      <ShopButton variant="primary" type="submit">Pay $7</ShopButton>      <ShopButton disabled aria-label="Remove Chai">Remove</ShopButton>    </>  );}
ResultLoading…

Line 1 takes variant, with a default, and collects every other prop into rest with the ... rest syntax. Line 2 uses variant for the class and spreads rest onto the real <button>. So line 8's type="submit" and line 9's disabled and aria-label reach the button. So do the words Pay $7 and Remove: they're children, and children is a prop like any other, so it travels in rest. Line 9's disabled with no value means disabled={true}; the second button is greyed out.

Spread is short to write, and that's also its risk. This card spreads everything it gets onto its element:

Spreading too much onto an element (warnings on purpose)Pop out in the code editor (opens in a new tab)JSX
function TeaCard({ name, ...rest }) {  return (    <article className="tea-card" {...rest}>      <h3>{name}</h3>    </article>  );} const chai = { name: "Chai", price: 4, inStock: true, className: "sale" }; export default function MenuPage() {  return <TeaCard {...chai} />;}
ResultLoading…

The result looks almost right, but three things went wrong. Press Edit & run to read the console:

  1. Stray attributes. price isn't an HTML attribute, but it lands on the page as price="4". Right-click the card and inspect it to see it.
  2. Warnings. inStock can't be an attribute at all, so React warns: React does not recognize the `inStock` prop on a DOM element.
  3. Replaced values. Props are read left to right, and a later one wins. {...rest} comes after className="tea-card" on line 3, so the data's className: "sale" replaces it, and the card loses its border.

Spread is fine when you choose what goes in, as ShopButton does: it forwards button attributes to a button. Otherwise, name your props, so anyone reading the component can see what it takes. Also never spread an object that holds a key: React warns, and wants key written on the tag itself.

Props are read-only

A happy hour takes a dollar off every tea. The quickest idea is to change the price prop inside the card. Here it is, with React 19.3.1 in development, as every live example runs:

Writing to a prop (an error on purpose)Pop out in the code editor (opens in a new tab)JSX
function TeaCard(props) {  props.price = props.price - 1;  return (    <article className="tea-card">      <h3>{props.name}</h3>      <p>Happy hour: ${props.price}</p>    </article>  );} export default function MenuPage() {  return <TeaCard name="Chai" price={4} />;}
ResultLoading…

The result is an error: Cannot assign to read only property 'price' of object '#<Object>', from line 2. In development, React freezes every props object with Object.freeze, so its properties can't be changed. Your component file is a module, and modules run in strict mode, where writing to a frozen property throws instead of failing quietly. Here is where React does it, in ReactJSXElement.js, the file that builds every element:

ReactJSXElement.js, lines 276 to 279, inside a development-only blockJavaScript
    if (Object.freeze) {
      Object.freeze(element.props);
      Object.freeze(element);
    }

These lines sit inside if (__DEV__), a block that only development builds keep. Read them at lines 276 to 279 of ReactJSXElement.js, release v19.3.1, and the block's start at line 240. Line 2 freezes the props; line 3 freezes the element that holds them.

A production build, the one your users download, leaves these lines out to save time. There, the same assignment runs without an error and the card shows Happy hour: $3. It looks fixed, but only that one render's object changed. The parent still has 4, and the next time MenuPage renders, it makes a brand-new props object with price: 4, and your change is gone. Code that only works in production is still a bug; development catches it for you.

Left: MenuPage passes price={4} down an arrow to TeaCard. Right, inside TeaCard: const sale = price - 1 is labelled reading, fine. props.price = price - 1 is labelled writing, TypeError.MenuPageprice={4}TeaCardinside TeaCardconst sale = price - 1;reading: fineprops.price = price - 1;writing: TypeError
Props flow one way: down, from parent to child. The child can read them and work out new values from them. Writing to them throws in development, because React freezes every props object there.

The fix is to leave the prop alone and work out a new value from it:

A new value worked out from the propPop out in the code editor (opens in a new tab)JSX
function TeaCard({ name, price }) {  const happyHourPrice = price - 1;  return (    <article className="tea-card">      <h3>{name}</h3>      <p>Happy hour: ${happyHourPrice}</p>    </article>  );} export default function MenuPage() {  return <TeaCard name="Chai" price={4} />;}
ResultLoading…

Line 2 makes happyHourPrice from price, and line 6 shows it: Happy hour: $3. The prop is untouched. (The destructured price is a plain local variable, so reassigning it wouldn't touch the props object either, but a new name says what the value means.)

The freeze is shallow: it covers the props object itself, not objects inside it. So this card, which changes a property of an object prop, gets no error at all:

Changing an object inside props (a bug on purpose)Pop out in the code editor (opens in a new tab)JSX
const chai = { name: "Chai", price: 4 }; function TeaCard({ tea }) {  tea.price = tea.price - 1;  return (    <article className="tea-card">      <h3>{tea.name}</h3>      <p>Happy hour: ${tea.price}</p>    </article>  );} export default function MenuPage() {  return <TeaCard tea={chai} />;}
ResultLoading…

Line 4 takes a dollar off tea.price, and tea is chai from line 1: the parent's own object, not a copy (the Objects and references lesson explains why). The result says Happy hour: $2, two dollars off, not one. StrictMode called TeaCard twice, and each call took off another dollar from the same object. That double call exists to make exactly this kind of bug show up. In production there's one call per render, so it says $3 at first, then drops a dollar every time the card renders again.

How to read this table: each row asks one question about writing to props, in development and in a production build.

Writing to props, in development and in production
QuestionIn developmentIn a production build
Is the props object frozen?Yes. React calls Object.freeze on it.No. React skips the freeze to save time.
What does props.price = 3 do?It throws a TypeError, because a module's code runs in strict mode.It changes this render's object, and nothing else. The parent still has its own value.
What does tea.price = 3 do, when tea is a prop?It works, because freezing is shallow. StrictMode's second call takes off another dollar, which shows the bug.It works, and changes the parent's object, so every later render sees the changed price.
What should you write instead?A new value: const happyHourPrice = price - 1;.The same. Code that only works in production is still a bug.

Props that change over time

Read-only doesn't mean fixed forever. A component often shows different props over time: a cart badge's count goes up with every tea. The change happens in the parent, which passes new props. Here the parent keeps the count in state, data React remembers between renders, as in Why React?:

A cart badge whose count comes from its parentPop out in the code editor (opens in a new tab)JSX
import { useState } from "react"; function CartBadge({ count }) {  return <p>Cart: <strong>{count}</strong> {count === 1 ? "item" : "items"}</p>;} export default function ShopHeader() {  const [count, setCount] = useState(0);  return (    <header>      <CartBadge count={count} />      <button onClick={() => setCount(count + 1)}>Add a tea</button>      <button onClick={() => setCount(0)}>Empty cart</button>    </header>  );}
ResultLoading…

Line 8 keeps count in ShopHeader, starting at 0. Line 11 passes it to CartBadge, which only reads it (line 4). Click Add a tea: line 12 calls setCount(count + 1), React calls ShopHeader again, and line 11 now passes count={1}. React calls CartBadge with a new props object, and the badge says Cart: 1 item. Empty cart sets it back to 0. The State: a component's memory lesson covers useState fully.

Does the badge get the same props object with a new number in it, or a new object? Find out below: the playground keeps every props object CartBadge renders with, and reads each one again after every click.

Playground: a new props object for every render
CartBadge and its parentJSX
import { useState } from "react"; function CartBadge({ count }) {  return <p>Cart: <strong>{count}</strong> {count === 1 ? "item" : "items"}</p>;} export default function ShopHeader() {  const [count, setCount] = useState(0);  return (    <header>      <CartBadge count={count} />      <button onClick={() => setCount(count + 1)}>Add a tea</button>      <button onClick={() => setCount(0)}>Empty cart</button>    </header>  );}
The page0 renders

Cart: 0 items

    Try it yourself

    CartBadge has rendered once, with one props object. Press Add a tea.

    CartBadge renders on this page for real. Each row is a props object it received, kept as the object itself and read again now: an old object still holds the count it was made with. The last six are shown.

    Each render brings a new object, and the old ones still say exactly what they said: { count: 0 } stays 0. React never changes props. It renders the parent again, which makes new elements with new props, and the old objects are dropped. So props are a snapshot: the parent's data at the moment of one render.

    Three props objects in a row, joined by arrows labelled Add a tea: count 0 in render 1 and count 1 in render 2, both crossed out as dropped, and count 2 in render 3, the one in use now.{ count: 0 }render 1{ count: 1 }render 2Add a tea{ count: 2 }render 3, nowAdd a tea
    Each click makes ShopHeader render again, and React hands CartBadge a new props object. The old objects aren't changed; React stops using them, and they're dropped.

    A tea menu wired with props

    Here is everything at once, in three files like a real project: App.jsx, Panel.jsx, and TeaCard.jsx, in tabs. Click Add to cart on two teas:

    A tea menu wired with propsPop out in the code editor (opens in a new tab)JSX
    import { useState } from "react";import Panel from "./Panel.jsx";import TeaCard from "./TeaCard.jsx"; export default function MenuPage() {  const [cart, setCart] = useState([]);  const add = (name) => setCart([...cart, name]);   return (    <main>      <Panel title={`Your cart (${cart.length})`}>        <p>{cart.length === 0 ? "Nothing yet." : cart.join(", ")}</p>      </Panel>      <Panel title="Today's teas">        <TeaCard name="Green tea" price={3} onAdd={add} />        <TeaCard name="Chai" price={4} size="Large" onAdd={add} />        <TeaCard name="Iced lemon tea" price={5} soldOut onAdd={add} />      </Panel>    </main>  );}
    ResultLoading…
    • Line 6 keeps the cart in state, and line 7 makes add, a function that puts a name in a new cart array.
    • Lines 11 to 13: the first Panel's title comes from a template literal, Your cart (2) after two clicks, and its children is a paragraph listing the cart.
    • Lines 15 to 17: three TeaCards. Line 15 uses the size default, line 16 passes size="Large", and line 17 writes soldOut with no value, which passes true. Each gets add as onAdd.
    • In the TeaCard.jsx tab, line 1 sets two defaults, and line 6 calls onAdd(name) on a click, so the card tells the parent which tea, and the parent changes the cart.

    Data flows down as props: from MenuPage into each Panel and TeaCard. Changes go up as calls to functions the parent passed down, and come back down as new props.

    Reads props, or changes them?
    • const total = price * 2;
    • props.price = 5;
    • const label = size ?? "Regular";
    • tea.price = tea.price - 1;
    • const sorted = items.toSorted();
    • items.push("Cake");
    • const { price, ...others } = props;
    • items.sort();
    Try it yourself
    0 of 8 correct

    Each line sits inside a component whose props are price, size, tea (an object), and items (an array). Sort it: does it only read props, or change them?

    Choose a category for every card. You can change an answer at any time; Reset clears them all.

    Common mix-ups

    • "Props are strings, like HTML attributes." No. Quotes pass a string, but braces pass any value: numbers, objects, arrays, functions. price="3" and price={3} are different props.
    • "A component can change its props to update the screen." No. Props are read-only. The parent changes its own data, usually state, and passes new props.
    • "A default is used whenever a prop is empty." No. Only when it's missing or undefined. null, "", and 0 are kept.
    • "defaultProps sets defaults." Not on function components in React 19: it's ignored. Use a default in the destructuring.
    • "children is special syntax." No. It's an ordinary prop that holds what's between the tags, and it only shows where the component writes {children}.
    • "Spreading props is always safe." No. Unknown props become attributes or warnings, and a later prop replaces an earlier one with the same name.
    • "No error means it's fine to change props." No. Production builds don't freeze props, and objects inside props are never frozen. The bug is still there.

    Practice exercises

    Each exercise shows its result under the code, or when you press Edit & run. Change the code, and the result updates as you type. Try it yourself first, then use the hints.

    Exercise 1 · Warm-upPredict the size

    What does the card's paragraph show? Answer first, then press Edit & run to check.

    function TeaCard({ name, price, size = "Regular" }) {
      return (
        <article className="tea-card">
          <h3>{name}</h3>
          <p>{size}: ${price}</p>
        </article>
      );
    }
    
    const tea = { name: "Oolong", price: 4 };
    
    export default function MenuPage() {
      return <TeaCard name={tea.name} price={tea.price} size={tea.size} />;
    }

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 2 · Warm-upGive each card its own tea

      All three cards say Green tea. Make the cards show Green tea $3, Chai $4, and Mint tea $2, with props.

      function TeaCard() {  return (    <article className="tea-card">      <h3>Green tea</h3>      <p>$3</p>    </article>  );} export default function MenuPage() {  return (    <main>      <TeaCard />      <TeaCard />      <TeaCard />    </main>  );}
      ResultLoading…

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 3 · PracticePlace the children

        The tea guide's cards show only their titles. Make each card show the paragraph written between its tags.

        function Card({ title }) {  return (    <section className="panel">      <h3>{title}</h3>    </section>  );} export default function TeaGuide() {  return (    <>      <Card title="Green tea">        <p>Steep for 3 minutes.</p>      </Card>      <Card title="Black tea">        <p>Steep for 5 minutes.</p>      </Card>    </>  );}
        ResultLoading…

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 4 · PracticeStop changing the order

          Happy hour takes $1 off, so the $4 tea should cost $3, but the line says $2. Fix it without changing order.

          const order = { tea: "Green tea", price: 4 }; function OrderLine({ order }) {  order.price = order.price - 1;  return <p>{order.tea}, happy hour: ${order.price}</p>;} export default function Receipt() {  return <OrderLine order={order} />;}
          ResultLoading…

          Answer, then press Check. Spacing and letter case don’t matter.

            Exercise 5 · ChallengeForward the button's props

            The Pay button should submit the form, and Remove Chai should be disabled, but ShopButton drops both props. Make it forward every prop except label to its button.

            function ShopButton({ label }) {  return <button className="btn">{label}</button>;} export default function Checkout() {  return (    <form>      <ShopButton label="Pay $7" type="submit" />      <ShopButton label="Remove Chai" disabled />    </form>  );}
            ResultLoading…

            Answer, then press Check. Spacing and letter case don’t matter.

              Exercise 6 · ChallengeAsk the parent for new props

              Clicking Add a tea does nothing. Make it work, keeping the count in ShopHeader. What does the cart say after two clicks?

              import { useState } from "react"; function CartBadge({ count }) {  return (    <p>      Cart: <strong>{count}</strong>{" "}      <button onClick={() => { count = count + 1; }}>Add a tea</button>    </p>  );} export default function ShopHeader() {  const [count, setCount] = useState(0);  return <CartBadge count={count} />;}
              ResultLoading…

              Answer, then press Check. Spacing and letter case don’t matter.

                Check your understanding

                For each code question, write down the props object each component gets first, with each value's type. Press Edit & run on a question's code to check your answer.

                The props quiz · 7 questionsScore: first tries count
                1. Question 1 of 7What does this price tag show?

                  Read the code, then predictPop out in the code editor (opens in a new tab)JSX
                  function PriceTag({ price }) {
                    return <p>Two cups: ${price * 2}, with a cookie: ${price + 1}</p>;
                  }
                  
                  export default function App() {
                    return <PriceTag price="4" />;
                  }

                  Choose an answer to see the explanation.

                2. Question 2 of 7What does this badge show between the brackets?

                  Read the code, then predictPop out in the code editor (opens in a new tab)JSX
                  function Badge({ label = "New" }) {
                    return <p>[{label}]</p>;
                  }
                  
                  export default function App() {
                    return <Badge label={null} />;
                  }

                  Choose an answer to see the explanation.

                3. Question 3 of 7What does this page show from the Panel?

                  Read the code, then predictPop out in the code editor (opens in a new tab)JSX
                  function Panel({ title }) {
                    return <section><h3>{title}</h3></section>;
                  }
                  
                  export default function App() {
                    return (
                      <Panel title="Today's special">
                        <p>Chai, $4</p>
                      </Panel>
                    );
                  }

                  Choose an answer to see the explanation.

                4. Question 4 of 7In development, what happens when a component runs props.price = 5?

                  Choose an answer to see the explanation.

                5. Question 5 of 7And in a production build, what does the same props.price = 5 do?

                  Choose an answer to see the explanation.

                6. Question 6 of 7What type does this Save button get?

                  Read the code, then predictPop out in the code editor (opens in a new tab)JSX
                  function SaveButton(props) {
                    return <button type="button" {...props}>Save</button>;
                  }
                  
                  export default function App() {
                    return <SaveButton type="submit" />;
                  }

                  Choose an answer to see the explanation.

                7. Question 7 of 7ShopHeader re-renders with its count at 2. What does CartBadge get?

                  Choose an answer to see the explanation.

                Key takeaways

                • Props are how a parent passes data to a component. React gathers a tag's props into one object and calls the component with it.
                • Read props with destructuring, function TeaCard({ name, price }), and give missing ones defaults with =. Defaults apply to missing and undefined props, not null.
                • Markup between a component's tags arrives as the children prop, and shows where the component places it.
                • Spread passes an object's properties as props, and rest forwards the ones you don't use. Choose what you spread: extra props land on elements, and later props win.
                • Props are read-only. React freezes them in development, so writing to one throws; production skips the freeze, but the write is still a bug. New data comes as new props from the parent, every render.

                Remember the one-liner.
                Props flow down as a read-only snapshot; to change them, the parent passes new ones.

                Coming next: Conditional rendering. It uses props to decide what a component shows, with if, the ternary operator, &&, and null.

                CompleteFrontend Clear concepts. Working examples.