Props vs State
The two kinds of data a component works with — read-only input from outside, and mutable memory it owns itself.
2 min read
Every React component works with two distinct kinds of data, and mixing them up is one of the most common sources of confusion for beginners: props and state.
Props: read-only input
Props are how a parent component passes data into a child — arguments to a function, effectively:
function Avatar({ src, size }) {
return <img src={src} width={size} height={size} />;
}
<Avatar src="/ada.png" size={64} />;Props always flow in one direction, from parent to child, and a component must never modify its own props:
function Avatar({ size }) {
size = size * 2; // Never do this
return <img width={size} />;
}If Avatar needs a different value, that's a sign the parent should pass a different prop — components should treat their props as a fixed snapshot for that render, the same way a function shouldn't reassign its own parameters.
State: a component's own memory
State is data a component owns and can change over time, created with the useState hook (the next lesson covers it in full):
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}Unlike a prop, state is private to the component that declares it — no parent can see or set another component's state directly — and calling its setter function is what tells React to re-render with the new value.
Why the distinction matters
| | Props | State | |---|---|---| | Owned by | The parent | The component itself | | Can it change? | No, from inside the component | Yes, via its setter function | | Who can modify it | Only the parent, by re-rendering with new values | Only the component itself | | Purpose | Configure a component from outside | Track something that changes over time |
A component can have both at once — a Counter might receive a step prop from its parent (how much to increment by) while tracking count as its own internal state:
function Counter({ step }) {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + step)}>{count}</button>;
}Getting this distinction right early avoids a common bug pattern: trying to "sync" a prop into state with useState(propValue) and then wondering why updates to the prop don't show up (the state was only initialized from the prop once, on mount, and never updates itself again — the two are not linked).
Next up: what happens when two sibling components both need access to the same state.