Passing Props to a Component
Props are just the one object argument React hands every component. Most of it is obvious once you see that. Here are the parts I want to actually remember.
It's one object, and I destructure it
A component gets exactly one argument — props. So function Avatar({ person, size }) isn't React syntax; it's plain JS destructuring of that object.
function Avatar(props) { … }
function Avatar({ person, size }) { … }
And because it's an object, defaults are just JS defaults: function Avatar({ size = 100 }). Worth remembering that a default only kicks in for undefined. Pass size={0} or size={null} and that value stays — the default won't step in.
{...props} is just spread
<Avatar {...props} /> reads like magic but it's the object spread I already know, dumping keys in as attributes. Fine for a thin wrapper that forwards everything. The catch is it hides what the child actually receives, so I try not to reach for it out of laziness.
children is a prop like any other
Whatever I put between the tags shows up as children:
<Card><Avatar /></Card>
Card gets props.children set to <Avatar />. This is the whole reason composition works — a Card or a layout doesn't need to know what it's wrapping, only that there's a children-shaped hole to drop it into. That's why wrappers stay generic.
Putting the three ideas together — destructuring with a default, children, and props flowing down from the parent:
// props are ONE object — destructure it, defaults are plain JS
function Avatar({ person, size = 100 }) {
return <img src={person.url} width={size} />;
}
// children is just a prop
function Card({ children }) {
return <div className="card">{children}</div>;
}
function App() {
return (
<Card>
<Avatar person={{ url: "/ajay.jpg" }} size={64} />
</Card>
);
}
Props are a read-only snapshot
I don't mutate props — the parent owns them. If a value needs to change over time, that's state, not props.
The bit that took me a while to internalize: props are immutable per render. Every render gets its own frozen copy. A component never watches props change in place — when the parent passes something new, React just re-runs the child with a fresh snapshot and throws the old props away. Data flows down, and a change means a re-render. That's the entire model.
If I want a value to change over time, I don't try to reach up and mutate the prop — I ask the parent to pass a different one, and the parent uses state for that. Props are how the parent talks down; state is what makes the parent say something new.