Props vs State in React — the real difference
Props flow into a component from its parent. State is owned by the component and changes inside it.
Props — read-only inputs
function Greeting({ name, color }) {
// name and color came from the parent — DO NOT mutate
return <h1 style={{ color }}>Hello, {name}</h1>;
}
// Parent
<Greeting name="Alice" color="purple" />
If the parent passes a new value, the child re-renders with the new value. The child can't change props.
State — owned by the component, triggers re-render on change
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
count is private to Counter. Calling setCount schedules a re-render.
Side-by-side
| Props | State | |
|---|---|---|
| Owned by | Parent | The component itself |
| Mutable? | No (read-only) | Yes (via setter) |
| Triggers re-render? | Parent re-renders the child with new props | Yes — own re-render |
| Use for | Configuration, callbacks, data flowing down | UI state, user input, fetched data |
Passing data UP — callbacks via props
State lives in one place. Children fire callbacks via props; parent updates state.
function NameForm({ onSubmit }) {
const [value, setValue] = useState('');
return (
<form onSubmit={(e) => { e.preventDefault(); onSubmit(value); }}>
<input value={value} onChange={(e) => setValue(e.target.value)} />
<button>Save</button>
</form>
);
}
function App() {
const [savedName, setSavedName] = useState('');
return (
<>
<NameForm onSubmit={setSavedName} /> {/* callback prop */}
<p>Saved: {savedName}</p>
</>
);
}
value is local to NameForm, but savedName is lifted up to App. Children communicate up via callbacks.
"Lifting state up"
If two siblings need to share data, put the state in their closest common parent and pass it down via props.
function App() {
const [theme, setTheme] = useState('light');
return (
<>
<Header theme={theme} onChangeTheme={setTheme} />
<Body theme={theme} />
</>
);
}
Common mistakes
// ❌ Trying to mutate a prop
function Toggle({ value }) {
value = !value; // does nothing useful; React doesn't track
return <p>{value ? 'on' : 'off'}</p>;
}
// ❌ Initializing state from a prop that may change
function Profile({ name }) {
const [displayName] = useState(name);
// If parent updates `name`, displayName stays stale forever
}
// ✅ If you need to "snapshot" a prop, use a ref or sync via useEffect — but
// usually just render the prop directly.
When state should become props (lifted)
If your component is "controlled" by its parent (form input with both value AND onChange), it's a controlled component — its value is a prop, not state. The parent owns the truth.
Quick test
Could two instances of this component have different values for this field at the same time?
- Yes → state (it's per-instance UI)
- No, always determined by the parent → prop