Topic 2.3
Passing Props
In one line
Props are the inputs of a component: the parent writes them like attributes, React collects them into one object, and the child reads that object (usually by destructuring) to decide what to show. Any JavaScript value can be a prop: strings, numbers, booleans, objects, arrays, functions and JSX.
Think of it like this
Ordering at a juice stall. The juice maker (the component) always works the same way, but your order changes the result: "mango, large, no ice". Props are the order slip. The parent writes the order, the component reads it and makes exactly that. The juice maker doesn't change your slip; it only reads it.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Props
- The inputs of a component, written like attributes by the parent:
<Avatar name="Asha" />. - Props object
- The single object React builds from those attributes and passes to your function:
{ name: "Asha" }. - Destructuring props
- Pulling fields out of the props object in the parameter list:
function Avatar({ name }). - Expression
- Any piece of JavaScript that produces a value. In JSX it goes inside
{ }. - Callback prop
- A function passed as a prop so the child can tell the parent something happened, like
onBuy. - Boolean shorthand
- Writing a prop with no value,
<Toggle on />, which meanson={true}. - Reserved prop
key(andrefbefore React 19): React uses them itself and doesn't hand them to your component.
Step by step
01A component that can't change
Greeting always says the same thing. To greet different people you would need a new component for each. Props fix that.
function Greeting() {
return <h2>Hello, Asha!</h2>;
}02The parent passes a prop
The parent adds an attribute. The value can be anything; a string goes in quotes.
<Greeting name="Asha" />
<Greeting name="Ravi" />03React builds the props object
For each tag, React collects the attributes into an object and calls your function with it. <Greeting name="Ravi" /> becomes Greeting({ name: "Ravi" }).
04The child reads it
Destructure the field you need and use it in the JSX. Now one component greets anyone.
function Greeting({ name }) {
return <h2>Hello, {name}!</h2>;
}05Pass other types with braces
Numbers, booleans, objects, arrays, functions and JSX all go inside { }. Only plain strings may use quotes.
<ProductCard
name="Mango juice" // string
price={60} // number
inStock // boolean true (shorthand)
tags={["cold", "fresh"]} // array
seller={{ name: "Ravi" }} // object
onBuy={() => alert("Bought!")} // function
badge={<strong>New</strong>} // JSX
/>06New props, new render
When the parent renders again with different values, React calls the child again with the new props object, and the screen follows. The child never stores or changes its props itself.
Try it yourself
- 1
Add a third greeting
In the first example, add
<Greeting name="Meera" age={10} />. Predict the new console line before running. - 2
Pass a new prop
Give
ProductCardaratingprop (a number from 1 to 5) and show that many stars with"★".repeat(rating). Passrating={4}to the mango juice. What happens to the lassi card if you don't pass it? ("★".repeat(undefined)is an empty string, so no stars.) - 3
Forget the braces in the parameter
In the first example, change
function Badge({ label, color })tofunction Badge(label, color). Predict what appears, then run it. (Nothing sensible:labelis now the whole props object and React throws because objects can't be shown.)
Code & diagrams
Each `<Greeting />` gets its own props object. The string came from quotes, the number from braces.
Preview
Console
Nothing logged yet.Expected console output
Greeting got { name: 'Asha', age: 9 }
Greeting got { name: 'Ravi', age: 11 }One component, seven kinds of props. `onBuy` is a function: the child calls it on click, and the parent (which owns the message) updates. `badge={null}` shows nothing.
Preview
Console
Nothing logged yet.`points="5"` passed the text "5", so `+ 1` glued the strings together. `points={5}` passed a number. React never converts prop types for you.
Preview
Console
Nothing logged yet.Expected console output
string points + 1 = 51
number points + 1 = 6Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Render an object prop directly
In "Every kind of value as a prop", change Sold by {seller.name} from {seller.city} to Sold by {seller}.
Break #2
Pass `"false"` in quotes
Change the lassi's inStock={false} to inStock="false".
Break #3
Try to read `key` as a prop
Render <Greeting key="a" name="Asha" age={9} /> and log props.key inside Greeting.
Myth vs fact
Myth
Props can only be strings, like HTML attributes.
Fact
Props can be any JavaScript value: numbers, booleans, objects, arrays, functions, JSX, even other components.
Myth
A child can change a prop to update the parent.
Fact
Props are read-only. To change the parent's data, the child calls a function the parent passed in (onBuy), and the parent updates its own state.
Myth
<Toggle on="false" /> turns it off.
Fact
That passes the truthy string "false". Use on={false} or leave the prop out.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
<Card a={1} />compiles tojsx(Card, { a: 1 }). React creates a new props object on every render of the parent, even when every value is the same. That's whymemocompares props field by field (shallowly) instead of by object identity (Topic 16.3). - ▸
Inline objects, arrays and arrow functions (
seller={{...}},onBuy={() => ...}) are new values on every render. That's harmless by default, but it defeatsmemoon the child and can re-run effects that depend on them;useMemo/useCallbackor the React Compiler fix it when it matters (Phase 16). - ▸
In development, React freezes the props object (
Object.freeze). Assigning to a prop in module code (which is always strict mode) throwsTypeError: Cannot assign to read only property, so mutations are caught early (Topic 2.6). - ▸
In React 19,
refis an ordinary prop for function components andforwardRefis no longer needed for new code.keyremains special and is never visible to the component.
Remember this
- 1
Props (short for *properties*) are how a parent passes data to a child. You write them like HTML attributes:
<Avatar name="Asha" size={64} />. React gathers every attribute into one plain object,{ name: "Asha", size: 64 }, and passes it as the first argument of your function. That object is called the props object. - 2
You can read it as
function Avatar(props) { return <p>{props.name}</p>; }, but most code destructures it right in the parameter list:function Avatar({ name, size }). It's the same object; destructuring just pulls out the fields you need and shows, at a glance, which props the component expects. Forgetting the braces (function Avatar(name)) is a common slip:nameis then the whole props object. - 3
Strings can be written in quotes:
title="Hello". Every other type needs curly braces, which mean "this is a JavaScript expression": numberssize={64}, booleansfeatured={true},null, variablesname={user.name}, calculationsprice={qty * 10}. Writingsize="64"passes the string "64", and"64" + 1is"641", a silent bug. A prop written with no value,<Toggle on />, is shorthand foron={true}. Leaving a prop out entirely givesundefined. - 4
Objects and arrays pass with braces too:
user={{ name: "Asha", city: "Pune" }}(outer braces mean "expression", inner braces are the object) andtags={["new", "sale"]}. You can't put an object directly on the screen ({user}throws "Objects are not valid as a React child"); show its fields instead:{user.name}. Arrays of strings or numbers render as text side by side; arrays of objects are turned into elements withmap(Topic 3.2). - 5
Functions are props too:
onBuy={handleBuy}. The parent decides *what happens*; the child decides *when to call it* (for example on a click). This is how a child talks back to its parent while data still flows down only (one-way data flow, Topic 0.2). By convention these props are namedonSomething; Topic 4.1 covers event handlers in depth. JSX is just a value, so it can be a prop as well:icon={<StarIcon />}lets the parent decide what to show inside a slot of the child (Topic 2.5 builds on this). - 6
Two attribute names are reserved and never reach your component:
key(used by React to tell list items apart, Topic 3.3) and, before React 19,ref. In React 19refis passed to function components as a normal prop (Topic 8.3). Props are read-only inside the child: you read them, you never assign to them (Topic 2.6). When the parent renders again with new values, the child receives a new props object and renders again with it.
Explain it without notes
What are props, and how does a component receive them?
When do you use quotes and when braces for a prop's value?
How does a child component tell its parent that something happened?
Why do some developers say "props flow down, events flow up"?
Practice
Write a Weather component that receives city (string), temp (number) and sunny (boolean) and shows Pune: 31°C ☀️ or Pune: 24°C ☁️. Render it for two cities.
Write a Tags component that receives an array items and shows them joined with · . Render it with three tags.
Trade-offs
- ↔
Many small props (
name,city) make a component's needs explicit and easy to change; one object prop (user) is shorter to pass but hides which fields are used and changes identity whenever the parent rebuilds it. - ↔
Passing JSX as a prop gives the parent full control of a slot, but too many slot props make a component hard to understand. Topic 2.5 shows when
childrenis the cleaner choice.
Done when you can
I can pass props from a parent and read them in a child.
I can destructure props in the parameter list.
I know when to use quotes and when braces.
I can pass objects, arrays, functions and JSX as props.
I can let a child report an event through a callback prop.
I know that
keyis not a prop and that props are read-only.