Props and Data Flow in React
Props (short for properties) let a parent component pass data to a child component. They work like arguments to a function. Whatever the parent passes in, the child receives and uses to render its output.
A component cannot change its own props. Props are read-only. If you need data that changes over time, that is what state is for.
Passing props
You pass props the same way you write HTML attributes:
function App() {
return (
<UserCard name="Alice" age={28} isAdmin={true} />
);
}
The child receives all props as a single object:
function UserCard(props) {
return (
<div>
<h2>{props.name}</h2>
<p>Age: {props.age}</p>
{props.isAdmin && <span>Admin</span>}
</div>
);
}
Destructuring props
Most React code destructures props directly in the function signature. It is cleaner and saves you from writing props. everywhere:
function UserCard({ name, age, isAdmin }) {
return (
<div>
<h2>{name}</h2>
<p>Age: {age}</p>
{isAdmin && <span>Admin</span>}
</div>
);
}
Both approaches produce identical output. Destructuring is the standard pattern in modern React code.
Default prop values
Use JavaScript default parameter syntax to set a fallback when a prop is not passed:
function Greeting({ name = 'World' }) {
return <h1>Hello, {name}!</h1>;
}
// Without a prop:
<Greeting /> // Hello, World!
// With a prop:
<Greeting name="Alice" /> // Hello, Alice!
Passing different types
You can pass any JavaScript value as a prop. Use curly braces for everything except strings:
| Type | Example |
|---|---|
| String | title="Hello" |
| Number | count={42} |
| Boolean | isActive={true} or just isActive |
| Array | items={['a', 'b', 'c']} |
| Object | user={{ name: 'Alice', age: 28 }} |
| Function | onClick={handleClick} |
Passing a function as a prop is how child components communicate back to their parent:
function Button({ label, onClick }) {
return <button onClick={onClick}>{label}</button>;
}
function App() {
function handleClick() {
console.log('Button clicked');
}
return <Button label="Click me" onClick={handleClick} />;
}
The Button component does not need to know what handleClick does. It just calls whatever function it receives.
The children prop
When you write content between a component’s opening and closing tags, React passes it as a special prop called children:
function Card({ children }) {
return <div className="card">{children}</div>;
}
function App() {
return (
<Card>
<h2>Card Title</h2>
<p>Some text inside the card.</p>
</Card>
);
}
The children prop is how you build wrapper components: layouts, modals, panels, and anything else that wraps content it does not know about in advance.
One-way data flow
Data in React flows in one direction: from parent to child. A child component cannot directly change data in its parent.
If a child needs to trigger a change in the parent, the parent passes a handler function down as a prop, and the child calls it:
import { useState } from 'react';
function Counter({ count, onIncrement }) {
return (
<div>
<p>Count: {count}</p>
<button onClick={onIncrement}>Increment</button>
</div>
);
}
function App() {
const [count, setCount] = useState(0);
return (
<Counter
count={count}
onIncrement={() => setCount(count + 1)}
/>
);
}
The parent owns the state. The child only displays it and calls the handler when something happens. This pattern keeps data flow predictable.
Props vs state
| Props | State | |
|---|---|---|
| Where it lives | Passed from the parent | Inside the component |
| Who controls it | The parent | The component itself |
| Can it change? | No (read-only) | Yes, with the setter function |
| Triggers re-render? | When parent re-renders | Yes, when the setter is called |
A simple rule: if data comes from outside the component, it is a prop. If the component manages it itself, it is state.
Common mistakes
Mutating a prop directly
// Wrong: props are read-only
function UserCard({ user }) {
user.name = 'Changed'; // do not do this
return <p>{user.name}</p>;
}
// Correct: derive a new value instead
function UserCard({ user }) {
const displayName = user.name.toUpperCase();
return <p>{displayName}</p>;
}
Calling a function instead of passing it
// Wrong: handleClick() runs immediately when the component renders
<button onClick={handleClick()}>Click</button>
// Correct: pass the function reference
<button onClick={handleClick}>Click</button>
Forgetting curly braces for non-string values
// Wrong: age becomes the string "28", not the number 28
<UserCard age="28" />
// Correct: use curly braces for numbers, booleans, arrays, objects
<UserCard age={28} />
What to read next
- Functional Components : props in detail, component composition, and the children pattern
- State : how to manage data that changes over time inside a component
- Lifting State Up : how to share state between sibling components