- Home
- /
- Tutorials
- /
- React Tutorial
- /
- Passing Props
Components and Props
Passing Props
Props are the arguments of a component. A parent passes them down; the child reads them and never writes to them.
Passing and reading
Anything after the component name in JSX becomes a property of one object, handed to the function as its first argument. Strings use quotes; everything else uses braces.
Every prop type
jsx
function Profile(props) {
return (
<div style={{ border: "1px solid #cbd5e1", padding: 12, borderRadius: 8 }}>
<strong>{props.name}</strong> — {props.role}
<p>Visits: {props.visits}</p>
<p>Admin: {props.isAdmin ? "yes" : "no"}</p>
<p>Tags: {props.tags.join(", ")}</p>
</div>
)
}
const root = ReactDOM.createRoot(document.getElementById("root"))
root.render(
<Profile
name="Ada"
role="Engineer"
visits={12}
isAdmin={true}
tags={["maths", "computing"]}
/>
)name="Ada" and name={"Ada"} mean the same thing. Quotes are the shorthand for a string, and everything else - numbers, booleans, arrays, objects, functions - needs braces.
Destructuring in the signature
Writing props. in front of every value gets noisy, so almost all real React destructures in the parameter list. It also documents what the component expects at a glance.
Destructured, with defaults
jsx
// The defaults apply when the prop is undefined.
function Button({ label, tone = "neutral", disabled = false }) {
const background = tone === "primary" ? "#007a96" : "#e2e8f0"
const color = tone === "primary" ? "#ffffff" : "#0f172a"
return (
<button
disabled={disabled}
style={{ background, color, border: 0, borderRadius: 6, padding: "6px 12px" }}
>
{label}
</button>
)
}
const root = ReactDOM.createRoot(document.getElementById("root"))
root.render(
<div style={{ display: "flex", gap: 8 }}>
<Button label="Default tone" />
<Button label="Primary" tone="primary" />
<Button label="Disabled" disabled={true} />
</div>
)Props are read-only
A component must never assign to its props. React does not stop you in every case, but the parent owns that value - writing to it means the parent's data and the screen disagree, and the change vanishes on the next render anyway.
Never do this
jsx
function Broken({ count }) {
count = count + 1 // the parent still has the old value
return <p>{count}</p>
}If a child needs a different value, it either derives one locally or asks the parent to change it through a function prop. That constraint is what makes one-way data flow work.
A default only fills in undefined
Destructuring defaults apply when the prop is undefined - not when it is null, 0, or an empty string. Passing count={0} gives you zero, not the default, which is almost always what you want and occasionally a surprise.
That is the same rule as any JavaScript default parameter, so there is nothing React-specific to remember. It does mean that if a parent passes null deliberately, the child receives null and has to handle it.
- Strings take quotes; everything else takes braces.
- Destructure in the signature and give defaults there.
- Props are read-only - derive, do not assign.
- A missing prop is
undefined, which is why defaults work. nulland0are values, so they skip the default.
