Ever picked up a TV remote? You've got buttons that show what's currently playing (like the channel number), and buttons that let you change what's playing.
React components work surprisingly similar - some data they just display, and other data they can actually change. That's basically the difference between props and state, but let's break it down properly.
Props and state are two of those React concepts that make perfect sense to experienced developers but leave the rest of us scratching our heads.
Even after watching countless tutorials, you might still be wondering: "When do I use props? When do I use state? What's the actual difference?"
I've been there. This is why I wrote this guide — no crazy jargon, no mad diagrams, just plain English explanations finally bringing these concepts together.
By the end of this post, you will have understood when to use props and state, and why either case is meaningful!
The Basics First
Before we jump into props and state, let's quickly understand why we need them in the first place.
React is built around components - think of them as LEGO blocks for your website...
You build small, reusable pieces that you can snap together to create something bigger. Maybe it's a navigation bar, a user profile card, or just a simple button.
But here's the thing: these components need to handle data somehow. Think about a simple user profile component:
- Sometimes you want to pass information into it (like username and avatar)
- Sometimes you want the component to manage its own information (like whether a dropdown menu is open or closed)
This is exactly why React gives us two different ways to handle data:
- Props: For passing data into components
- State: For components to manage their own data
It's like the difference between:
- Information someone tells you (props)
- Information you keep track of yourself (state)
That's really all there is to the basics. No need to overcomplicate it. Let's move on to understanding props in detail.
Props: The Messenger
Props are honestly pretty simple - they're just data that gets passed into your component from the outside.
Think of props like function parameters. Just like you can pass arguments to a function, you can pass props to a component. Here's what I mean:
// This is like a function that takes parameters
function Greeting(props) {
return <h1>Hey {props.name}!</h1>
}
// Using it is as simple as this
<Greeting name="Sarah" />
Here's what you need to know about props:
-
Props are Read-Only
- You can't change props inside a component
- If you try, React will yell at you (and rightly so)
- Think of them as incoming messages you can read but can't edit
-
Common Use Cases:
- Passing text content:
<Button text="Click Me" />
- Configuration:
<Modal size="large" position="center" />
- Passing data:
<UserProfile user={userData} />
- Handling events:
<Button onClick={handleClick} />
- Passing text content:
-
Props Flow Down
- Parent components pass props to child components
- Data only flows down, never up
- Yes, this means props are "one-way" data flow
Quick example to make it crystal clear:
function UserCard({ name, role, avatar }) {
return (
<div>
<img src={avatar} alt={name} />
<h2>{name}</h2>
<p>{role}</p>
</div>
);
}
// Using it
<UserCard
name="Alex"
role="Developer"
avatar="/alex.jpg"
/>
That's really all there is to props. They're just a way to pass data into components. Simple as that.
State: The Memory
State is your component's personal memory. While props are like receiving a message, state is like keeping your own notes that you can update whenever you want.
Here's the key thing about state: use it when your component needs to keep track of information that can change. Simple as that.
Let's see it in action:
import { useState } from 'react'
function Counter() {
const [count, setCount] = useState(0)
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
)
}
Here's what you really need to know about state:
-
State is Changeable
- Unlike props, you can (and should) update state
- Always use the setter function (like
setCount
) - Never modify state directly (
count = count + 1
is a big no-no)
-
State Changes Trigger Re-renders
- When state updates, your component refreshes
- React compares the old and new state
- Only changed parts get updated in the DOM
-
State is Private
- Each component manages its own state
- Parent components can't read or write child's state
- If you need to share state, lift it up to a parent
Common use cases for state:
// Form inputs
const [username, setUsername] = useState('')
// Toggles
const [isOpen, setIsOpen] = useState(false)
// Loading states
const [isLoading, setIsLoading] = useState(false)
// Data
const [items, setItems] = useState([])
Remember: If a piece of data needs to change over time - that's your cue to use state.
Props vs State: The Key Differences
Let's cut through the confusion and lay out exactly when to use each:
Props:
- Read-only data that comes from outside
- Can't be changed by the component
- Update only when the parent rerenders
// Props example
function Header({ username }) {
return <h1>Welcome {username}</h1>
// Can't change username here!
}
State:
- Internal data that the component controls
- Can (and should) be updated by the component
- Updates trigger a rerender
function Menu() {
const [isOpen, setIsOpen] = useState(false)
// We control this value!
return (
<button onClick={() => setIsOpen(!isOpen)}>
{isOpen ? 'Close' : 'Open'}
</button>
)
}
Quick Decision Guide:
- Need to pass data down? → Props
- Need to track changing values? → State
- Data coming from parent? → Props
- Component needs memory? → State
Think of it this way:
- Props are like receiving a photo - you can display it but can't edit it
- State is like taking a photo - you own it and can change it
That's really the core difference.
Common Gotchas & Best Practices
Let's tackle the most common headaches and how to avoid them:
Prop Drilling Issues:
// DON'T do this 🚫
<GrandParent>
<Parent>
<Child>
<GrandChild userProfile={userProfile} />
</Child>
</Parent>
</GrandParent>
// DO this instead ✅
// Use React Context or state management for deeply nested data
const UserContext = createContext()
<UserContext.Provider value={userProfile}>
// Now any child can use useContext(UserContext)
</UserContext.Provider>
State Management Mistakes:
// DON'T update state directly 🚫
setCount(count + 1)
setCount(count + 1) // Won't work as expected!
// DO use functional updates ✅
setCount(prev => prev + 1)
setCount(prev => prev + 1) // Works correctly
When to Lift State Up:
// If multiple components need the same state
function Parent() {
const [user, setUser] = useState(null)
return (
<>
<Header user={user} />
<Profile user={user} />
<Settings user={user} setUser={setUser} />
</>
)
}
Quick Best Practices:
- Keep state as local as possible
- Use props for read-only data flow
- Lift state up when siblings need to share data
- Break down components when they get too complex
- Use controlled components for forms
Practical Examples
Let's look at two common scenarios that combine both props and state effectively:
1) A Toggle Button Component:
function ToggleButton({ initialState, onToggle }) {
// Local state for the toggle
const [isOn, setIsOn] = useState(initialState)
const handleClick = () => {
setIsOn(!isOn)
onToggle(!isOn) // Prop function to notify parent
}
return (
<button
onClick={handleClick}
className={isOn ? 'active' : 'inactive'}
>
{isOn ? 'ON' : 'OFF'}
</button>
)
}
// Using it
function App() {
const handleToggle = (state) => {
console.log('Toggle is now:', state)
}
return <ToggleButton
initialState={false}
onToggle={handleToggle}
/>
}
2) A Card Component with State:
function UserCard({ user, onEdit }) {
// Local state for expand/collapse
const [isExpanded, setIsExpanded] = useState(false)
return (
<div className="card">
<h3>{user.name}</h3>
<button onClick={() => setIsExpanded(!isExpanded)}>
{isExpanded ? 'Show Less' : 'Show More'}
</button>
{isExpanded && (
<div className="details">
<p>{user.email}</p>
<p>{user.role}</p>
<button onClick={() => onEdit(user)}>
Edit Profile
</button>
</div>
)}
</div>
)
}
// Using it
function UserList() {
const [users, setUsers] = useState([
{ id: 1, name: 'John', email: 'john@example.com', role: 'Admin' }
])
const handleEdit = (user) => {
// Handle edit logic
}
return (
<div>
{users.map(user => (
<UserCard
key={user.id}
user={user}
onEdit={handleEdit}
/>
))}
</div>
)
}
See how these examples combine both concepts?
- Props for passing data and callbacks
- State for managing component-specific behavior
- Clear separation of concerns
- Parent components stay in control of important data
- Child components handle their own UI state
Conclusion
Let's wrap this up with the key takeaways:
Props vs State in a nutshell:
- Props: Pass data down, can't be changed
- State: Component's memory, meant to change
The mental model to remember:
- Think of props like orders at a restaurant - you can't change what the customer ordered
- Think of state like the kitchen's inventory - the kitchen controls and updates it
When to use what:
// Use props when:
<UserProfile
username={user.name} // Displaying data
avatar={user.image} // Passing configurations
onSave={handleSave} // Passing callbacks
/>
// Use state when:
function Form() {
const [input, setInput] = useState('') // Form inputs
const [isValid, setIsValid] = useState(true) // UI states
const [data, setData] = useState(null) // API data
}
Next steps to level up:
- Practice building components that combine both
- Learn about Context for global state
- Explore state management libraries when needed
- Master useEffect for side effects
Remember: There's no shame in re-reading docs or checking examples. Even experienced devs do it. The key is understanding the core concepts, which you now do!
Props and state might seem tricky at first, but they're just tools to help you build better React apps. Now go build something awesome! 🚀
Before you go, I share actionable tips, tutorials, and resources daily on X.
Follow me let's connect and keep learning from each other!
Top comments (0)