# Props

Course: [React](https://www.devtutorials.ai/learn/react) · Section: Data Flow in React
Source: https://www.devtutorials.ai/learn/react/data-flow/props

> What props are, passing one or many props, destructuring, typing props with an interface, optional and default props, and passing objects, functions and children as props.

## Introduction to Props

**Props** are values that you send into a component. The word props is short for **properties**.

Props let you use one component many times, with different data each time. A parent component sends props. A child component receives them.

### Real-World Example

A bookshop's `BookList` component gets 20 books from the server. It passes the data of each book to a `BookCard`: `<BookCard title={book.title} price={book.price} />`.

`BookCard` does not know where the data comes from. It only displays the props it receives, so the same card also works on the search page and the wishlist page.

<figure className="react-fig">
<svg viewBox="0 0 520 160" role="img" aria-labelledby="fig-props-title" style={{ width: "100%", maxWidth: 520, height: "auto" }}>
  <title id="fig-props-title">The parent App sends title and price as props to the child BookCard</title>
  <rect x="10" y="40" width="160" height="80" rx="10" style={{ fill: "var(--accent-soft)", stroke: "var(--accent)", strokeWidth: 1.5 }} />
  <text x="90" y="74" textAnchor="middle" style={{ fill: "var(--accent)", fontSize: 15, fontWeight: 700, fontFamily: "var(--sans)" }}>App</text>
  <text x="90" y="96" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 12, fontFamily: "var(--sans)" }}>Parent: sends props</text>
  <rect x="350" y="40" width="160" height="80" rx="10" style={{ fill: "var(--accent-secondary-soft)", stroke: "var(--accent-secondary)", strokeWidth: 1.5 }} />
  <text x="430" y="74" textAnchor="middle" style={{ fill: "var(--accent-secondary)", fontSize: 15, fontWeight: 700, fontFamily: "var(--sans)" }}>BookCard</text>
  <text x="430" y="96" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 12, fontFamily: "var(--sans)" }}>Child: shows props</text>
  <path d="M174 80 L342 80" style={{ stroke: "var(--faint)", strokeWidth: 2, fill: "none" }} />
  <path d="M334 73 L344 80 L334 87 Z" style={{ fill: "var(--faint)" }} />
  <text x="260" y="58" textAnchor="middle" style={{ fill: "var(--muted)", fontSize: 12, fontFamily: "var(--mono)" }}>title="Atomic Habits"</text>
  <text x="260" y="108" textAnchor="middle" style={{ fill: "var(--muted)", fontSize: 12, fontFamily: "var(--mono)" }}>price=&#123;499&#125;</text>
  <circle r="6" style={{ fill: "var(--accent)" }}>
    <animateMotion dur="2s" repeatCount="indefinite" path="M174 80 L342 80" />
  </circle>
</svg>
<figcaption>Props move one way: from the parent down to the child.</figcaption>
</figure>

Props go from parent to child. A child can read its props, but it must not change them.

## Passing Data with Props

To send a prop, write it like an HTML attribute on the component tag. To read it, the child takes a `props` object as its input.

    </div>
  );
}
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

The same `Greeting` component shows two different names. `name="Asha"` sends the text "Asha". Inside the child, `props.name` reads it.

Text values go in quotes. All other values go in curly braces: numbers `{499}`, true or false `{true}`, and variables `{bookTitle}`.

## Using Multiple Props

A component can receive many props. Write them one after another on the tag, in any order.

      <h3>{props.title}</h3>
      <p>by {props.author}</p>
      <p>₹{props.price}</p>
    </div>
  );
}

  return (
    <div>
    </div>
  );
}
`,
    "App.css": `.card { border: 1px solid #cbd5e1; border-radius: 8px; padding: 2px 12px; margin-bottom: 8px; }
.card h3 { margin: 8px 0 4px; }
.card p { margin: 4px 0; }
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

React puts all the props into one object. For the first card, the object is `{ title: "The Alchemist", author: "Paulo Coelho", price: 299 }`.

## Props Destructuring

React puts all the props in one object, called `props`. To read a prop, you write `props.title` or `props.price`.

**Destructuring** is a shorter way. You write the prop names inside `{ }` in the function's brackets. Then you can use each prop by its name, without `props.` in front.

<figure className="react-fig ngc-fig">
<svg viewBox="0 0 520 170" role="img" aria-labelledby="fig-props-destr" style={{ width: "100%", maxWidth: 600, height: "auto" }}>
  <title id="fig-props-destr">Animation: the props object is unpacked into separate title and price variables</title>
  <defs><marker id="pd" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M0 0 L10 5 L0 10 z" style={{ fill: "var(--ngc-purple)" }} /></marker></defs>
  <rect x="10" y="50" width="180" height="70" rx="8" style={{ fill: "var(--ngc-blue-soft)", stroke: "var(--ngc-blue)", strokeWidth: 1.5 }} /><text x="100" y="82" textAnchor="middle" style={{ fill: "var(--ngc-blue)", fontSize: 12, fontWeight: 700, fontFamily: "var(--sans)" }}>props (one object)</text><text x="100" y="96" textAnchor="middle" style={{ fill: "var(--ngc-blue)", fontSize: 11, fontWeight: 400, fontFamily: "var(--mono)" }}>&#123; title, price &#125;</text>
  <g opacity="0"><animate attributeName="opacity" values="0;0;1;1;0" keyTimes="0;0.15;0.19;0.93;1" dur="8s" repeatCount="indefinite" /><path d="M 192 75 L 300 40" style={{ fill: "none", stroke: "var(--ngc-purple)", strokeWidth: 2 }} markerEnd="url(#pd)" /><path d="M 192 95 L 300 130" style={{ fill: "none", stroke: "var(--ngc-purple)", strokeWidth: 2 }} markerEnd="url(#pd)" /><text x="245" y="90" textAnchor="middle" style={{ fill: "var(--ngc-purple)", fontSize: 11, fontWeight: 700, fontFamily: "var(--sans)" }}>&#123; title, price &#125;</text></g>
  <g opacity="0"><animate attributeName="opacity" values="0;0;1;1;0" keyTimes="0;0.3;0.34;0.93;1" dur="8s" repeatCount="indefinite" /><rect x="305" y="18" width="200" height="44" rx="8" style={{ fill: "var(--ngc-green-soft)", stroke: "var(--ngc-green)", strokeWidth: 1.5 }} /><text x="405" y="36" textAnchor="middle" style={{ fill: "var(--ngc-green)", fontSize: 12, fontWeight: 700, fontFamily: "var(--sans)" }}>title</text><text x="405" y="52" textAnchor="middle" style={{ fill: "var(--ngc-green)", fontSize: 11, fontWeight: 400, fontFamily: "var(--mono)" }}>"Atomic Habits"</text></g>
  <g opacity="0"><animate attributeName="opacity" values="0;0;1;1;0" keyTimes="0;0.38;0.42;0.93;1" dur="8s" repeatCount="indefinite" /><rect x="305" y="108" width="200" height="44" rx="8" style={{ fill: "var(--ngc-green-soft)", stroke: "var(--ngc-green)", strokeWidth: 1.5 }} /><text x="405" y="126" textAnchor="middle" style={{ fill: "var(--ngc-green)", fontSize: 12, fontWeight: 700, fontFamily: "var(--sans)" }}>price</text><text x="405" y="142" textAnchor="middle" style={{ fill: "var(--ngc-green)", fontSize: 11, fontWeight: 400, fontFamily: "var(--mono)" }}>499</text></g>
  <g opacity="0"><animate attributeName="opacity" values="1;1;0;0;1" keyTimes="0;0.5;0.55;0.95;1" dur="8s" repeatCount="indefinite" /><text x="260" y="164" textAnchor="middle" style={{ fill: "var(--ngc-blue)", fontSize: 12, fontWeight: 700, fontFamily: "var(--sans)" }}>Before: props.title and props.price</text></g><g opacity="0"><animate attributeName="opacity" values="0;0;1;1;0" keyTimes="0;0.5;0.55;0.95;1" dur="8s" repeatCount="indefinite" /><text x="260" y="164" textAnchor="middle" style={{ fill: "var(--ngc-green)", fontSize: 12, fontWeight: 700, fontFamily: "var(--sans)" }}>After: just title and price</text></g>
</svg>
<figcaption>Destructuring takes each prop out of the props object, so you can use it by its name.</figcaption>
</figure>

```tsx
// Without destructuring
function BookCard(props: { title: string; price: number }) {
  return <p>{props.title} — ₹{props.price}</p>;
}

// With destructuring
function BookCard({ title, price }: { title: string; price: number }) {
  return <p>{title} — ₹{price}</p>;
}
```

Both components show the same thing. Look at the difference:

- **Without destructuring**, the function gets one object called `props`. You read `props.title`.
- **With destructuring**, `{ title, price }` takes `title` and `price` out of the object. You read just `title`.

Most React code uses the second way, because it is shorter.

## Defining and Typing Props

In TypeScript, you write down which props a component needs and what kind of value each one is. TypeScript then warns you if a prop is missing or has the wrong kind of value.

### Typing Props Using an Interface

An **interface** is a named list of props and their types. You write it once, above the component.

```tsx
interface BookCardProps {
  title: string;   // text
  price: number;   // number
  inStock: boolean; // true or false
}

function BookCard({ title, price, inStock }: BookCardProps) {
  return <p>{title} — ₹{price} — {inStock ? "In stock" : "Sold out"}</p>;
}
```

You can also use `type BookCardProps = { ... }`. For props, both work the same way. This course uses `interface` for props from now on.

### Optional Props

Some props are not always needed. Put a `?` after the name to make a prop **optional**. The parent can then leave it out.

```tsx
interface BookCardProps {
  title: string;
  subtitle?: string; // optional
}
```

If the parent does not send an optional prop, its value is `undefined`.

### Default Props

A **default value** is the value a prop gets when the parent does not send it.

You set it in the function's brackets, with `=`. In the code below, look at this part of `BookCard.tsx`:

```tsx
function BookCard({ title, price, subtitle, inStock = true }: BookCardProps)
```

`inStock = true` means: if the parent does not send `inStock`, use `true`. If the parent sends `inStock={false}`, use `false`.

      <h3>{title}</h3>
      {subtitle && <p className="sub">{subtitle}</p>}
      <p>₹{price} · {inStock ? "In stock" : "Sold out"}</p>
    </div>
  );
}
`,
    "App.tsx": `import BookCard from "./BookCard";

  return (
    <div>
    </div>
  );
}
`,
    "App.css": `.card { border: 1px solid #cbd5e1; border-radius: 8px; padding: 2px 12px; margin-bottom: 8px; }
.card h3 { margin: 8px 0 4px; }
.card p { margin: 4px 0; }
.sub { color: #64748b; font-style: italic; }
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

Look at the three cards:

- **Atomic Habits** has a subtitle, because the parent sent one.
- **Wings of Fire** shows "Sold out", because the parent sent `inStock={false}`.
- **The Alchemist** has no subtitle and shows "In stock". The parent sent neither prop, so the default `true` was used.

Look at this line in `BookCard.tsx`:

```tsx
{subtitle && <p className="sub">{subtitle}</p>}
```

It means: **if** `subtitle` has a value, show the paragraph. If the parent did not send a subtitle, nothing is shown. That is why only "Atomic Habits" has a subtitle line.

`?` means "this prop can be left out". `= value` means "use this value when it is left out".

## Passing Objects as Props

A prop can hold a whole object. This is useful when the data already comes as one object, such as a book from a list.

      <h3>{book.title}</h3>
      <p>by {book.author} · ₹{book.price}</p>
    </div>
  );
}
`,
    "App.tsx": `import BookCard, { type Book } from "./BookCard";

const featured: Book = {
  title: "Wings of Fire",
  author: "A. P. J. Abdul Kalam",
  price: 350,
};

  return (
    <div>
      <h2>Book of the week</h2>
    </div>
  );
}
`,
    "App.css": `.card { border: 1px solid #cbd5e1; border-radius: 8px; padding: 2px 12px; }
.card h3 { margin: 8px 0 4px; }
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

The parent sends one prop, `book`. The child reads its parts with `book.title`, `book.author` and `book.price`.

The `Book` interface is exported, so `App` can use the same type for its data.

## Passing Functions as Props

A prop can also be a **function**. The parent sends a function, and the child calls it. This is how a child tells its parent that something happened, such as a button click.

<figure className="react-fig ngc-fig">
<svg viewBox="0 0 520 200" role="img" aria-labelledby="fig-props-fn" style={{ width: "100%", maxWidth: 600, height: "auto" }}>
  <title id="fig-props-fn">Animation: App passes the onBuy function down to BuyButton; the click calls it, and App shows the bought book</title>
  <defs><marker id="pf-b" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M0 0 L10 5 L0 10 z" style={{ fill: "var(--ngc-blue)" }} /></marker></defs><defs><marker id="pf-o" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M0 0 L10 5 L0 10 z" style={{ fill: "var(--ngc-orange)" }} /></marker></defs>
  <rect x="160" y="10" width="200" height="48" rx="8" style={{ fill: "var(--ngc-purple-soft)", stroke: "var(--ngc-purple)", strokeWidth: 1.5 }} /><text x="260" y="30" textAnchor="middle" style={{ fill: "var(--ngc-purple)", fontSize: 12, fontWeight: 700, fontFamily: "var(--sans)" }}>App (parent)</text><text x="260" y="46" textAnchor="middle" style={{ fill: "var(--ngc-purple)", fontSize: 11, fontWeight: 400, fontFamily: "var(--mono)" }}>function onBuy(title)</text>
  <rect x="160" y="140" width="200" height="48" rx="8" style={{ fill: "var(--ngc-green-soft)", stroke: "var(--ngc-green)", strokeWidth: 1.5 }} /><text x="260" y="160" textAnchor="middle" style={{ fill: "var(--ngc-green)", fontSize: 12, fontWeight: 700, fontFamily: "var(--sans)" }}>BuyButton (child)</text><text x="260" y="176" textAnchor="middle" style={{ fill: "var(--ngc-green)", fontSize: 11, fontWeight: 400, fontFamily: "var(--mono)" }}>Buy Atomic Habits</text>
  <g opacity="0"><animate attributeName="opacity" values="0;0;1;1;0" keyTimes="0;0.05;0.09;0.93;1" dur="9s" repeatCount="indefinite" /><path d="M 220 60 L 220 136" style={{ fill: "none", stroke: "var(--ngc-blue)", strokeWidth: 2 }} markerEnd="url(#pf-b)" /><text x="150" y="100" textAnchor="middle" style={{ fill: "var(--ngc-blue)", fontSize: 12, fontWeight: 700, fontFamily: "var(--sans)" }}>onBuy=&#123;onBuy&#125;</text></g>
  <g opacity="0"><animate attributeName="opacity" values="0;0;1;1;0" keyTimes="0;0.35;0.39;0.93;1" dur="9s" repeatCount="indefinite" /><rect x="170" y="166" width="180" height="18" rx="5" style={{ fill: "none", stroke: "var(--ngc-orange)", strokeWidth: 2 }} /><text x="440" y="170" textAnchor="middle" style={{ fill: "var(--ngc-orange)", fontSize: 12, fontWeight: 700, fontFamily: "var(--sans)" }}>user clicks</text></g>
  <g opacity="0"><animate attributeName="opacity" values="0;0;1;1;0" keyTimes="0;0.45;0.49;0.93;1" dur="9s" repeatCount="indefinite" /><path d="M 300 136 L 300 62" style={{ fill: "none", stroke: "var(--ngc-orange)", strokeWidth: 2 }} markerEnd="url(#pf-o)" /><text x="400" y="100" textAnchor="middle" style={{ fill: "var(--ngc-orange)", fontSize: 12, fontWeight: 700, fontFamily: "var(--sans)" }}>onBuy("Atomic Habits")</text></g>
  <g opacity="0"><animate attributeName="opacity" values="0;0;1;1;0" keyTimes="0;0.62;0.66;0.93;1" dur="9s" repeatCount="indefinite" /><rect x="380" y="14" width="130" height="40" rx="8" style={{ fill: "var(--ngc-orange-soft)", stroke: "var(--ngc-orange)", strokeWidth: 1.5 }} /><text x="445" y="30" textAnchor="middle" style={{ fill: "var(--ngc-orange)", fontSize: 12, fontWeight: 700, fontFamily: "var(--sans)" }}>Bought:</text><text x="445" y="46" textAnchor="middle" style={{ fill: "var(--ngc-orange)", fontSize: 11, fontWeight: 400, fontFamily: "var(--sans)" }}>Atomic Habits</text></g>
</svg>
<figcaption>The parent sends a function down. The child calls it to tell the parent that something happened.</figcaption>
</figure>

    </div>
  );
}
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

Press a button and look at the console. Here is what happens:

1. `App` sends its function `handleBuy` to the child as the prop `onBuy`.
2. You click the button inside `BuyButton`.
3. `BuyButton` calls `onBuy(title)`.
4. `handleBuy` runs inside `App` and prints the message.

Function props usually start with `on`, such as `onBuy`, `onClick` or `onSave`.

Pass the function itself: `onBuy={handleBuy}`. Do not write `onBuy={handleBuy()}`. The brackets would run the function at once, before any click.

## Children Props

**children** is a special prop. It holds whatever you put **between** the opening and closing tags of a component.

This lets you make wrapper components, such as a card or a box, that can hold any content.

<figure className="react-fig ngc-fig">
<svg viewBox="0 0 520 190" role="img" aria-labelledby="fig-props-children" style={{ width: "100%", maxWidth: 600, height: "auto" }}>
  <title id="fig-props-children">Animation: the content written between the Card tags moves into the Card as the children prop</title>
  <rect x="10" y="60" width="170" height="70" rx="8" style={{ fill: "var(--ngc-blue-soft)", stroke: "var(--ngc-blue)", strokeWidth: 1.5 }} /><text x="95" y="92" textAnchor="middle" style={{ fill: "var(--ngc-blue)", fontSize: 12, fontWeight: 700, fontFamily: "var(--sans)" }}>Between the tags</text><text x="95" y="106" textAnchor="middle" style={{ fill: "var(--ngc-blue)", fontSize: 11, fontWeight: 400, fontFamily: "var(--mono)" }}>&lt;p&gt;20% off&lt;/p&gt;</text>
  <rect x="270" y="20" width="240" height="150" rx="12" style={{ fill: "var(--ngc-purple-soft)", stroke: "var(--ngc-purple)", strokeWidth: 1.5 }} /><text x="390" y="42" textAnchor="middle" style={{ fill: "var(--ngc-purple)", fontSize: 13, fontWeight: 700, fontFamily: "var(--sans)" }}>Card title="Offer"</text><rect x="290" y="62" width="200" height="90" rx="8" style={{ fill: "none", stroke: "var(--ngc-purple)", strokeWidth: 1.5, strokeDasharray: "6 4" }} /><text x="390" y="165" textAnchor="middle" style={{ fill: "var(--ngc-purple)", fontSize: 11, fontWeight: 700, fontFamily: "var(--sans)" }}>&#123;children&#125; goes here</text>
  <g><rect x="10" y="60" width="170" height="70" rx="8" style={{ fill: "var(--ngc-green-soft)", stroke: "var(--ngc-green)", strokeWidth: 1.5 }} /><text x="95" y="92" textAnchor="middle" style={{ fill: "var(--ngc-green)", fontSize: 12, fontWeight: 700, fontFamily: "var(--sans)" }}>children</text><text x="95" y="106" textAnchor="middle" style={{ fill: "var(--ngc-green)", fontSize: 11, fontWeight: 400, fontFamily: "var(--mono)" }}>&lt;p&gt;20% off&lt;/p&gt;</text><animateTransform attributeName="transform" type="translate" values="0 0;0 0;305 22;305 22;0 0" keyTimes="0;0.3;0.55;0.93;1" dur="8s" repeatCount="indefinite" /><animate attributeName="opacity" values="0;0;1;1;0" keyTimes="0;0.28;0.3;0.93;1" dur="8s" repeatCount="indefinite" /></g>
</svg>
<figcaption>Whatever you put between the opening and closing tags arrives in the component as children.</figcaption>
</figure>

      <h3>{title}</h3>
      <div className="card-body">{children}</div>
    </div>
  );
}
`,
    "App.tsx": `import Card from "./Card";

  return (
    <div>
        <p>Every day, 9 am to 9 pm.</p>

        <ul>
          <li>Atomic Habits</li>
          <li>Wings of Fire</li>
        </ul>
    </div>
  );
}
`,
    "App.css": `.card { border: 1px solid #cbd5e1; border-radius: 8px; padding: 2px 12px; margin-bottom: 8px; }
.card h3 { margin: 8px 0 4px; color: #0047ba; }
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

The first card holds a paragraph. The second card holds a list. `Card` does not know what is inside. It just puts `{children}` in its body.

In the interface, `children` has the type `ReactNode`. This type allows any content that React can show on the screen. For example:

- text, like `Hello`
- a number, like `42`
- a tag, like `<p>...</p>` or `<ul>...</ul>`
- many tags together

So you can put any of these between `<Card>` and `</Card>`.

## Common mistakes

<div className="tscroll">

| Mistake | What you see | Fix |
|---|---|---|
| Sending a number in quotes: `price="499"` | The price is text, and maths on it gives wrong results | Use braces: `price={499}` |
| Changing a prop inside the child: `price = 100` | TypeScript error, or the change is lost | Treat props as read-only |
| Forgetting the braces when destructuring: `function Card(title)` | `title` is the whole props object, not the text | Write `function Card({ title })` |
| Missing a required prop | TypeScript warns that the prop is missing | Send the prop, or add `?` to make it optional |
| Calling the function when passing it: `onBuy={handleBuy()}` | It runs at once, before any click | Pass the function: `onBuy={handleBuy}` |
| Not showing `{children}` inside the wrapper | The content between the tags does not appear | Add `{children}` where it should show |
| Spelling a prop differently in parent and child | The value is `undefined` in the child | Use the same name in both places |

</div>

## Check Yourself

## Exercises

## What to Remember

- Props send data from a parent to a child. The child reads props but does not change them.
- Describe props with an interface. Use `?` for optional props and `= value` for defaults.
- Props can be text, numbers, objects, functions, or `children`.

Next: State
