Skip to main content

Props vs State in React

Answer

Props and State are both plain JavaScript objects that control a component's output, but they have different purposes and behaviors.

Key Differences

AspectPropsState
OriginFrom parent componentInternal to component
MutableNo (read-only)Yes (via setState/setter)
PurposeConfigurationInteractive data
UpdatesParent changes themComponent changes itself
Re-renderYes, when props changeYes, when state changes

Props Example

// Parent passes props
function App() {
return <UserCard name="John" age={30} isAdmin={true} />;
}

// Child receives props
function UserCard({ name, age, isAdmin }) {
// ❌ Cannot modify props
// name = "Jane"; // Wrong!

return (
<div>
<h2>{name}</h2>
<p>Age: {age}</p>
{isAdmin && <span>Admin</span>}
</div>
);
}

State Example

function Counter() {
const [count, setCount] = useState(0);

// ✅ Can modify state via setter
const increment = () => {
setCount(count + 1);
};

return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>+1</button>
</div>
);
}

Props for Configuration, State for Interaction

// Props configure initial behavior
// State handles user interaction
function SearchInput({ placeholder, onSearch }) {
const [query, setQuery] = useState("");

const handleSubmit = () => {
onSearch(query); // Callback prop
};

return (
<div>
<input
placeholder={placeholder} // From props
value={query} // From state
onChange={(e) => setQuery(e.target.value)}
/>
<button onClick={handleSubmit}>Search</button>
</div>
);
}

// Usage
<SearchInput
placeholder="Search..." // Prop
onSearch={(q) => console.log(q)} // Callback prop
/>;

Data Flow

function Parent() {
const [user, setUser] = useState({ name: "John" });

const updateName = (newName) => {
setUser({ ...user, name: newName });
};

return (
<div>
<DisplayUser user={user} /> {/* Props down */}
<EditUser user={user} onUpdate={updateName} /> {/* Callback up */}
</div>
);
}

function DisplayUser({ user }) {
return <h1>{user.name}</h1>;
}

function EditUser({ user, onUpdate }) {
return <input value={user.name} onChange={(e) => onUpdate(e.target.value)} />;
}

Common Patterns

Derived State from Props (Avoid!)

// ❌ Anti-pattern: Copying props to state
function User({ userProp }) {
const [user, setUser] = useState(userProp);
// Bug: Won't update when userProp changes!
}

// ✅ Good: Use prop directly or derive in render
function User({ user }) {
const fullName = `${user.firstName} ${user.lastName}`;
return <h1>{fullName}</h1>;
}

// ✅ Or use key to reset when prop changes
<UserForm user={user} key={user.id} />;

Lifting State Up

// Two children need same data? Lift state to parent

function Parent() {
const [temperature, setTemperature] = useState(20);

return (
<div>
<Thermometer temp={temperature} onChange={setTemperature} />
<TemperatureDisplay temp={temperature} />
</div>
);
}

Key Points

  • Props: External data passed in (read-only)
  • State: Internal data managed by component
  • Props flow down, callbacks flow up
  • Never modify props directly
  • State changes trigger re-renders
  • Lift state to share between siblings