React applications are built from components, and components often need to work with data. Two of the most important concepts for managing that data are props and state.
Props and state may look similar at first because both can contain values such as strings, numbers, arrays, and objects. However, they have different purposes.
Props are used to pass data into a component, while state is used to store data that a component needs to manage and update over time.
Understanding the difference between props and state is essential for building React applications that behave predictably and are easy to maintain.
Introduction
Imagine you are building a user profile card.
The parent component might provide the user’s:
- Name
- Profile picture
- Job title
- Location
That information can be passed to the profile card using props.
Now imagine the profile card also has a Follow button. Whether the user is currently followed may change when the button is clicked.
That changing value can be stored in state.
This gives us a simple way to think about the difference:
| Props | State |
|---|---|
| Passed into a component | Managed by the component |
| Usually comes from a parent | Usually belongs to the component that owns the data |
| Read-only from the receiving component’s perspective | Can be updated with a state setter |
| Used to configure a component | Used to remember changing information |
| Changes when the parent provides new values | Changes when the component updates its state |
The distinction becomes even clearer when you start building interactive applications.
What Are Props?
Props, short for properties, are values passed from one React component to another.
Props are most commonly passed from a parent component to a child component.
For example, a parent component can pass a user’s name to a child component:
function App() {
return <UserProfile name="Alex" />;
}
function UserProfile(props) {
return <h2>Hello, {props.name}</h2>;
}
Here, App is the parent component and UserProfile is the child component.
The name value is passed as a prop.
The child receives the value through the props object.
Props Are Read-Only
A component receiving props should not directly modify those props.
For example, this is not the correct approach:
function UserProfile(props) {
props.name = "John";
return <h2>{props.name}</h2>;
}
The child component should treat props.name as read-only.
If the value needs to change, the component that owns the data should update it and provide the new value through props.
This principle helps React applications maintain a predictable flow of data.
Props Can Change
It is important not to say that props can never change.
A child component cannot directly mutate its props, but the parent can render the child again with different prop values.
For example:
function App() {
const userName = "Alex";
return <UserProfile name={userName} />;
}
function UserProfile({ name }) {
return <h2>{name}</h2>;
}
If App later provides a different name, UserProfile receives the new prop value during the new render.
So the better rule is:
Props are read-only from the receiving component’s perspective, but their values can change when the parent provides different values.
What Is State?
State is data that a component needs to remember between renders and update in response to interaction or other events.
Modern React uses the useState Hook to manage local state in function components.
Here is a simple example:
import { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>
Increase
</button>
</div>
);
}
The count variable stores the current state value.
The setCount function updates that state.
When setCount is called, React schedules a new render of the component so the interface can reflect the updated value.
State Can Change
Unlike props, state is designed to be updated by the component that owns it.
For example:
const [isOpen, setIsOpen] = useState(false);
The component can update isOpen with:
setIsOpen(true);
Or toggle it:
setIsOpen(!isOpen);
For state updates that depend on the previous value, the updater form is often safer:
setIsOpen((previousValue) => !previousValue);
This tells React to calculate the next state from the previous state value.
Props vs State: The Core Difference
The easiest way to understand props and state is to ask:
Where does the data come from, and who should control it?
Props are supplied to a component from outside.
State is managed by the component that owns the state.
Consider a reusable button component.
function App() {
return (
<Button text="Save Changes" />
);
}
function Button({ text }) {
return <button>{text}</button>;
}
The text comes from the parent, so it is a prop.
Now imagine the button needs to track whether it has been clicked.
import { useState } from "react";
function Button({ text }) {
const [clicked, setClicked] = useState(false);
return (
<button onClick={() => setClicked(true)}>
{clicked ? "Saved" : text}
</button>
);
}
Here:
textis a prop.clickedis state.- The parent controls
text. - The button component controls
clicked.
Why Props and State Are Important
Props and state form a major part of how React components communicate and manage data.
Without props, reusable components would have difficulty receiving different data.
Without state, components would have difficulty remembering changing information such as:
- Whether a menu is open
- The current input value
- The selected tab
- Whether a user is logged in
- The current quantity in a shopping cart
- Whether a notification has been dismissed
- Loading or error status
Together, props and state allow components to be both reusable and interactive.
How Props Work
Props generally flow from a parent component to a child component.
Consider this example:
function App() {
return <Welcome name="Sarah" />;
}
function Welcome({ name }) {
return <h1>Welcome, {name}!</h1>;
}
The flow looks like this:
App → name prop → Welcome
The parent decides what value to provide.
The child decides how to use that value.
Passing Different Types of Props
Props are not limited to strings.
You can pass numbers, booleans, arrays, objects, functions, and other JavaScript values.
String Props
<User name="Sarah" />
Number Props
Use curly braces for JavaScript values such as numbers:
<User age={25} />
Boolean Props
<User isAdmin={true} />
Boolean shorthand can also be used:
<User isAdmin />
The shorthand is equivalent to:
<User isAdmin={true} />
Array Props
<ProductList products={["Laptop", "Mouse", "Keyboard"]} />
Object Props
<User
profile={{
name: "Sarah",
role: "Developer"
}}
/>
Function Props
Functions can also be passed as props:
<Button onClick={handleClick} />
This is especially useful when a child component needs to trigger an action controlled by its parent.
How State Works
State is commonly created with the useState Hook.
The basic syntax is:
const [stateValue, setStateValue] = useState(initialValue);
For example:
const [count, setCount] = useState(0);
There are three important parts:
countis the current state value.setCountis the function used to request an update.0is the initial value.
When the state setter is called, React schedules a re-render.
For example:
setCount(count + 1);
After React processes the update, the component renders with the new state value.
A Simple Props vs State Example
Let’s combine both concepts in one component.
import { useState } from "react";
function App() {
return <Profile name="Alex" />;
}
function Profile({ name }) {
const [isFollowing, setIsFollowing] = useState(false);
return (
<div>
<h2>{name}</h2>
<button onClick={() => setIsFollowing((value) => !value)}>
{isFollowing ? "Following" : "Follow"}
</button>
</div>
);
}
Step-by-Step Explanation
The App component passes "Alex" to Profile:
<Profile name="Alex" />
The Profile component receives the prop:
function Profile({ name }) {
The component creates local state:
const [isFollowing, setIsFollowing] = useState(false);
Initially, isFollowing is false.
The button displays:
{isFollowing ? "Following" : "Follow"}
Because the initial value is false, the button displays Follow.
When the user clicks the button, the state is updated:
setIsFollowing((value) => !value);
The value changes from false to true.
React renders the component again, and the button now displays Following.
The important distinction is that name comes from the parent as a prop, while isFollowing is managed locally as state.
Props and State in the Same Component
A component can receive props and maintain its own state at the same time.
This is extremely common in real React applications.
import { useState } from "react";
function ProductCard({ name, price }) {
const [quantity, setQuantity] = useState(1);
return (
<div>
<h2>{name}</h2>
<p>Price: ${price}</p>
<button onClick={() => setQuantity((value) => value - 1)}>
-
</button>
<span>{quantity}</span>
<button onClick={() => setQuantity((value) => value + 1)}>
+
</button>
</div>
);
}
Here:
nameis a prop.priceis a prop.quantityis state.
The parent provides the product information.
The product card manages the quantity.
This separation makes the component reusable.
For example:
<ProductCard name="Laptop" price={999} />
<ProductCard name="Keyboard" price={79} />
Both cards can use the same component while receiving different props and maintaining their own quantity state.
Parent State Passed as Props
A very common React pattern is to store state in a parent component and pass the current state value to a child as a prop.
import { useState } from "react";
function App() {
const [message, setMessage] = useState("Hello");
return <Message text={message} />;
}
function Message({ text }) {
return <p>{text}</p>;
}
Here, message is state owned by App.
The current value is passed to Message as a prop.
The data flow is:
App state → Message prop → rendered UI
This pattern is useful when multiple components need to use the same piece of data.
Using Callback Functions with Props
Sometimes a child needs to cause a change in data owned by its parent.
A child cannot directly modify the parent’s state.
Instead, the parent can pass a callback function to the child.
For example:
import { useState } from "react";
function App() {
const [count, setCount] = useState(0);
function increaseCount() {
setCount((value) => value + 1);
}
return <CounterButton onIncrease={increaseCount} />;
}
function CounterButton({ onIncrease }) {
return (
<button onClick={onIncrease}>
Increase
</button>
);
}
The parent owns the state.
The child receives a function through props.
When the button is clicked, the child calls the function.
The parent then updates its own state.
This creates a communication pattern:
Parent state → child prop
and
Child event → parent callback → parent state update
This is one of the most important patterns to understand when learning React.
Props vs State with Forms
Forms are another practical example.
A form input often needs changing data, so state is commonly used to store its current value.
import { useState } from "react";
function SearchBox() {
const [query, setQuery] = useState("");
return (
<input
value={query}
onChange={(event) => setQuery(event.target.value)}
placeholder="Search"
/>
);
}
Here, query is state because the value changes as the user types.
Now imagine a reusable search component:
function SearchBox({ placeholder }) {
const [query, setQuery] = useState("");
return (
<input
value={query}
onChange={(event) => setQuery(event.target.value)}
placeholder={placeholder}
/>
);
}
The placeholder is a prop because the parent can configure it.
The query is state because the component manages the user’s current input.
Props vs State with Objects
State can contain objects, but you should avoid directly mutating objects stored in state.
For example, avoid:
user.name = "Alex";
Instead, create a new object when updating state:
setUser((previousUser) => ({
...previousUser,
name: "Alex"
}));
The spread syntax copies the existing properties into a new object and replaces the name property.
This follows React’s recommended immutable update pattern.
Props vs State with Arrays
The same principle applies to arrays.
Avoid directly changing a state array with methods such as push().
For example, avoid:
items.push("React");
Instead, create a new array:
setItems((previousItems) => [
...previousItems,
"React"
]);
To remove an item, you can use methods such as filter():
setItems((previousItems) =>
previousItems.filter((item) => item !== "React")
);
The goal is to create a new array rather than directly modifying the existing state value.
Props and State with Lists
Props and state often work together when rendering lists.
For example:
import { useState } from "react";
function App() {
const [products, setProducts] = useState([
{ id: 1, name: "Laptop" },
{ id: 2, name: "Mouse" },
{ id: 3, name: "Keyboard" }
]);
return <ProductList products={products} />;
}
function ProductList({ products }) {
return (
<ul>
{products.map((product) => (
<li key={product.id}>
{product.name}
</li>
))}
</ul>
);
}
The products array is state in App.
It is then passed to ProductList as a prop.
The list uses map() to render each product.
Each item receives a stable key based on its id.
Stable keys help React correctly identify list items when the list changes.
If items can be reordered, inserted, or deleted, avoid using the array index as the key.
Props and State with the children Prop
children is a special prop that contains content placed between a component’s opening and closing tags.
For example:
function Card({ children }) {
return <div className="card">{children}</div>;
}
function App() {
return (
<Card>
<h2>React Props</h2>
<p>Props allow components to receive data.</p>
</Card>
);
}
The content inside <Card> is available through the children prop.
This is useful for creating reusable layouts and wrapper components.
The children prop can also work alongside state.
For example, a reusable expandable component could receive its content through children while managing whether that content is visible with state.
Real-World Use Cases
Props and state appear throughout practically every React application.
User Profile
Props can provide:
- Name
- Avatar
- Role
- Location
State can manage:
- Follow status
- Expanded profile information
- Edit mode
Shopping Cart
Props can provide:
- Product name
- Product image
- Product price
State can manage:
- Quantity
- Selected options
- Whether an item is expanded
Navigation Menu
Props can provide:
- Menu labels
- Links
- Icons
State can manage:
- Whether a mobile menu is open
- Which submenu is expanded
Dashboard
Props can provide:
- Chart data
- Titles
- Configuration options
State can manage:
- Selected date range
- Active filter
- Selected tab
Search Interface
Props can provide:
- Placeholder text
- Search configuration
- Initial settings
State can manage:
- Search query
- Loading status
- Selected filters
Common Mistakes
Trying to Change Props Directly
Avoid modifying props inside the receiving component.
Incorrect:
function User({ name }) {
name = "John";
return <h2>{name}</h2>;
}
If the value needs to change, the component that owns the data should control the update.
Using State for Everything
Not every value needs to be state.
For example, if a value can be calculated directly from existing props or state, you usually do not need another state variable for it.
Avoid unnecessary duplicated state.
Instead of storing both:
const [firstName, setFirstName] = useState("Alex");
const [fullName, setFullName] = useState("Alex Smith");
if fullName can be calculated from other existing data, consider deriving it instead.
Mutating State Directly
Avoid:
user.name = "Alex";
Use a state setter with an immutable update:
setUser((previousUser) => ({
...previousUser,
name: "Alex"
}));
Forgetting That State Updates Trigger Rendering
When state changes, React may render the component again.
You should not assume that changing the state variable directly will update the interface.
Use the setter:
setCount((value) => value + 1);
not:
count = count + 1;
Using Unnecessary State
If a value can be calculated from props or existing state during rendering, creating separate state for it can make your application harder to maintain.
For example:
const fullName = `${firstName} ${lastName}`;
There is no need to create separate state for fullName if it is always derived from firstName and lastName.
Best Practices
Keep Data Ownership Clear
Ask which component should own a piece of data.
If only one component needs changing data, local state may be appropriate.
If multiple components need the same changing data, you may need to move the state to their closest common parent and pass values and callbacks through props.
Keep Props Focused
Pass the data a component actually needs.
Instead of passing a huge object when only two properties are needed, consider passing those specific values.
This can make components easier to understand and reuse.
Use Descriptive Prop Names
Prefer:
<UserProfile name="Alex" />
over vague names such as:
<UserProfile data="Alex" />
Clear prop names make components easier to use.
Use Functional State Updates When Appropriate
When the next state depends on the previous state, use the updater form:
setCount((previousCount) => previousCount + 1);
This makes the dependency on the previous value explicit.
Avoid Duplicating State
If information can be derived from existing props or state, consider calculating it instead of storing another copy.
This reduces the possibility of different pieces of state getting out of sync.
A Realistic Web4Learners Example
Imagine Web4Learners has a React tutorial page containing a reusable ArticleCard component.
The parent component can provide article information through props:
function TutorialPage() {
return (
<ArticleCard
title="React Props vs State"
category="React"
readTime="8 min read"
/>
);
}
function ArticleCard({ title, category, readTime }) {
return (
<article>
<span>{category}</span>
<h2>{title}</h2>
<p>{readTime}</p>
</article>
);
}
The article card does not need to manage this information itself. The parent provides it.
Now suppose the card has a bookmark button.
The bookmark status can be local state:
import { useState } from "react";
function ArticleCard({ title, category, readTime }) {
const [bookmarked, setBookmarked] = useState(false);
return (
<article>
<span>{category}</span>
<h2>{title}</h2>
<p>{readTime}</p>
<button
onClick={() => setBookmarked((value) => !value)}
>
{bookmarked ? "Bookmarked" : "Bookmark"}
</button>
</article>
);
}
This creates a clean separation.
Props
The parent controls:
- Article title
- Category
- Reading time
State
The article card controls:
- Bookmark status
This pattern can be extended to real applications with likes, saved articles, expanded sections, filters, search boxes, and other interactive features.
Image Placement Instruction
Image placement: immediately after the section “Props vs State: The Core Difference”.
Create a clean 16:9 educational infographic titled “React Props vs State”.
Show two clearly separated sides:
Props
- Passed from parent to child
- Read-only in the receiving component
- Used to configure components
- Can change when the parent provides new values
- Example:
name="Alex"
State
- Managed by a component
- Updated with a state setter
- Used for changing data
- Triggers a re-render when updated
- Example:
const [count, setCount] = useState(0)
In the center or bottom, show a simple visual relationship:
Parent → Props → Child
and:
State → Component → UI Update
Use a modern developer-focused design with a clean light background, strong typography, subtle React-inspired visual elements, rounded code cards, and a polished Web4Learners-style educational appearance. Avoid excessive text and avoid making it look like an old-fashioned technical diagram.
Practice Exercise
Build a reusable ProductCard component.
The parent should pass these values as props:
- Product name
- Product price
- Product category
Inside ProductCard, create state for:
- Product quantity
Add two buttons:
- Increase quantity
- Decrease quantity
The quantity should never become less than 1.
A possible component usage could look like:
<ProductCard
name="Wireless Mouse"
price={29}
category="Accessories"
/>
Try to identify which values should be props and which value should be state before writing the component.
Challenge
Add an Add to Cart button.
When clicked, display a message such as:
Added to cart
Use state to control whether the message is displayed.
Props vs State Cheat Sheet
| Concept | Props | State |
|---|---|---|
| Purpose | Pass data into a component | Manage changing data |
| Ownership | Usually controlled by parent | Controlled by the component that owns it |
| Can child directly modify it? | No | Yes, through the state setter |
| Can value change? | Yes, when parent provides new props | Yes, through state updates |
| Common API | Component parameters | useState() |
| Example | name="Alex" | const [count, setCount] = useState(0) |
| Common use | Component configuration | User interaction and dynamic UI |
Key Takeaways
- Props allow components to receive data from outside, usually from a parent component.
- Props are read-only from the receiving component’s perspective.
- Props can receive strings, numbers, booleans, arrays, objects, functions, and React elements.
- A parent can provide different prop values on later renders.
- State stores information that a component needs to remember and update over time.
- The
useStateHook is commonly used to manage local state in function components. - State should be updated through its setter rather than by directly changing the state variable.
- Avoid directly mutating objects and arrays stored in state.
- Callback functions passed as props allow children to trigger actions in their parent.
childrenis a special prop containing nested JSX or other content.- Props and state are often used together in real React applications.
- If several components need the same changing data, state may need to be moved to their closest common parent and passed down through props.
- Keeping data ownership clear makes React components easier to understand, reuse, and maintain.