Props are how React passes data from a parent component down to its children, turning static markup into reusable, data-driven building blocks. This lesson covers how props are passed, accessed, and safely used while keeping React's data flow predictable.
1What are Props?
Props — short for properties — are React's mechanism for passing data from a parent component down to its children, similar to how you'd pass attributes to an HTML element. The key difference is that a prop's value isn't limited to strings: you can pass numbers, arrays, objects, functions, or any valid JavaScript value.
This is what makes components reusable — the same <User name="Lolly" role="Admin" /> component can render completely different content just by changing the props passed into it from its parent.
// Parent: <User name='Lolly' />Lolly
Admin
2Accessing Props
When a parent passes attributes to a child, React collects all of them into a single JavaScript object and passes that object as the first argument to the child's function — by convention named props. Inside the component, you access individual values with standard dot notation, like props.name, and inject them into JSX using curly braces.
So function User(props) { return <h1>Hello, {props.name}!</h1>; } reads the name value straight off the bundled props object and renders it as text.
function User(props) {
return <h1>Hello, {props.name}!</h1>;
}Hello, Lolly!
3Unidirectional Data Flow
React enforces a strictly unidirectional (one-way) data flow: data only ever moves downward, from parent components to their children. A child has no built-in way to send data back up to its parent or sideways to a sibling component.
This rigid architecture is actually a strength — because data only flows in one direction, it's always possible to trace exactly where a given value came from, which makes debugging state changes far simpler than in systems that allow data to flow freely in any direction.
// Parent -> Child Flow
// Prop: name='Lolly'Dashboard
State: { user: 'Lolly' }
UserProfile
Prop: name='Lolly'
4Props are Read-Only
Props are strictly read-only from the perspective of the component receiving them — a child must never attempt to modify or reassign a prop it was given, such as props.name = 'Hacked'; React will throw an error if you try.
If a value genuinely needs to change over time in response to user interaction, that change has to happen in the parent that owns the data (using State), which then passes the new value back down as a fresh prop — not by mutating the prop directly inside the child.
// ❌ NEVER DO THIS:
function User(props) {
props.name = 'Hacked';
}Props are Locked!
They are read-only and immutable.
5Destructuring Props
Repeatedly writing props.name, props.age, and so on gets tedious fast. Modern React code instead uses ES6 object destructuring directly in the function's parameter list — wrapping the parameter in curly braces, like function User({ name, age }), pulls exactly the values you need straight out of the props object.
This produces cleaner code and also documents, right in the function signature, exactly which props a component expects to receive.
function User({ name, age }) {
return <p>{name} is {age} years old.</p>;
}Alice is 25 years old.
6The Children Prop
React reserves a special prop called children for cases where you don't know a component's content ahead of time — think generic layouts, dialogs, or cards. Whatever JSX or text a parent places between a component's opening and closing tags is automatically captured in props.children.
Inside the component, you render {children} wherever that nested content should appear, as in function Card({ children }) { return <div className="card">{children}</div>; }.
<Card>
<h2>Exclusive</h2>
<p>Inside the children prop!</p>
</Card>I am injected!
Passed via the children prop.
7Passing Functions (Callbacks)
Since data can only flow downward, a child needs another mechanism to notify its parent that something happened, like a button being clicked. The answer is callbacks: the parent passes a function down to the child as a prop, and the child calls that function in response to an event, such as onClick.
This pattern is how child components safely trigger changes that live in a parent — the child never touches the parent's state directly, it just invokes the function the parent handed it.
function Card({ children }) {
return <div className='glass'>{children}</div>;
}8Default Props
If a parent forgets to pass a particular prop, a component can crash or render incorrectly. Default props guard against this using standard ES6 default parameter syntax inside the destructuring assignment, such as function Button({ color = 'blue' }).
When the parent doesn't supply a color prop at all, the component falls back to 'blue' automatically; if the parent does supply one, that value overrides the default.
<Button onAlert={() => alert('Triggered!')} />Prop Provided
No Prop (Fallback)
9The Spread Operator (...)
Passing many individual props one by one gets verbose fast. The JavaScript spread operator (...) lets you unpack an entire object and pass all of its key-value pairs as separate props in one shot: const config = { theme: 'dark', size: 'lg' }; <Layout {...config} /> passes both theme and size as though you'd written them out individually.
This is especially useful for forwarding configuration objects or wrapping native HTML elements without manually re-listing every prop.
function Button({ color = 'blue' }) {
return <button style={{ background: color }} />;
}Received Props:
- theme: "dark"
- size: "lg"
10Prop-Driven UIs
Putting these pieces together, a single reusable component can render dramatically different UI just by receiving different props — a <Card title="Revenue" value="$10k" /> and a <Card title="Users" value="452" /> share the exact same component definition but display entirely different data.
This is the core pattern behind how real applications manage large numbers of visual states predictably: build the structure once, and let props drive what actually gets displayed in each instance.
const config = { theme: 'dark', size: 'lg' };
<Layout {...config} />Revenue
$10k
Users
452
11Mastery Achieved
At this point you've covered the core mechanics of props: passing data dynamically from parent to child, respecting React's unidirectional data flow, capturing nested content with children, and using callback functions so children can notify their parents of events.
This is exactly the foundation you need before introducing State, which is what lets a component hold and update its own data over time rather than just receiving it from above.
/* Prop-Driven Dashboard Rendered */Props Complete ✓
12Mastery Achieved
With props mastered, you're ready to move from purely data-driven components to components that manage change over time. The next lesson introduces useState, the hook that gives a function component its own persistent memory.
Where props flow in from a parent and can never be changed by the receiver, state lives inside the component itself and can be updated directly by that component in response to user interaction.
/* Next: State & useState */Props Complete ✓
13Step-by-Step Breakdown
What are Props?. Welcome to Prop Flow. Props, which is short for 'properties', are the primary communication system within React. They allow you to pass dynamic data from a Parent component down to its Children, making your components reusable. Think of props just like custom HTML attributes, but instead of just strings, you can pass arrays, objects, functions, or any valid JavaScript data type.
Accessing Props. When a Parent passes props to a Child, React bundles all those attributes into a single JavaScript object and passes it as the first argument to the Child component's function. By convention, we name this parameter 'props'. Inside the component, you can use standard JavaScript dot notation (like props.name) to access the specific values and inject them directly into your JSX using curly braces.
Unidirectional Data Flow. React strictly enforces a Unidirectional (one-way) data flow. Data only ever flows downwards from Parents to Children. A child component has no direct way to send data 'up' to its parent or 'sideways' to its siblings. This rigid architecture is actually one of React's greatest strengths: it makes it incredibly easy to track exactly where data is coming from, vastly simplifying debugging and state management.
Props are Read-Only. This is a critical rule: Props are completely immutable (Read-Only) from the perspective of the child receiving them. A child component must NEVER attempt to modify or reassign its own props. If the child attempts to mutate props directly, React will throw an error. If data needs to change over time in response to user actions, you must use State in the parent, not mutate Props in the child.
Understanding React's data architecture is essential. In a React application, in which direction does data inherently flow between components?
- →Upwards (Child to Parent)
- →Downwards (Parent to Child)
Destructuring Props. Writing 'props.something' repeatedly gets extremely tedious. To write cleaner, more readable code, modern React developers utilize ES6 Object Destructuring directly in the function arguments. By wrapping the parameters in curly braces, you can immediately pull out exactly the variables you need from the props object, making your component's required inputs obvious at a single glance.
The Children Prop. Sometimes, you don't know your component's content ahead of time, like when building generic Layouts, Dialogs, or Cards. React provides a special, reserved prop named 'children'. The 'children' prop automatically captures whatever JSX elements or text the parent places between the opening and closing tags of your component. You can then render '{children}' anywhere inside your component's layout.
You are building a modal component and want to allow other developers to put custom forms or text inside of it. Which reserved prop name must you destructure to access the nested JSX?
- →content
- →children
Passing Functions (Callbacks). Because data only flows down, how does a child inform its parent that a button was clicked? The answer is Callbacks. You can pass a function from the parent down to the child as a prop. The child component can then execute that function when an event occurs (like an onClick). This pattern allows child components to safely trigger state changes up inside their parent components.
Default Props. Sometimes, parents might forget to pass a specific prop, which can cause your component to render incorrectly or crash. To prevent this, you can set Default Props. By utilizing standard ES6 default parameter syntax inside your destructuring assignment, you can ensure your component always falls back to a safe, default value if 'undefined' is passed down.
The Spread Operator (...). When passing many props down to a component, writing them all out one by one can become extremely verbose. React allows you to use the JavaScript ES6 Spread Operator (...) to automatically unpack an entire object and pass all of its key-value pairs as individual props. This is incredibly useful for passing down large configuration objects or wrapping native HTML elements.
Prop-Driven UIs. Observe the power of a Prop-Driven architecture. By abstracting the structure into a single reusable component, and passing different props to each instance, you can render drastically different UI elements with minimal code. This is how modern web applications manage thousands of complex visual states predictably.
Can a child component directly modify the properties (props) passed down to it from its parent component?
- →Yes, it can mutate them
- →No, they are strictly read-only
Mastery Achieved. Prop flow mastered! You've learned how to pass data dynamically, enforce unidirectional flow, and utilize children and callbacks. You now have the skills to build highly reusable component libraries. Get ready to add memory to these components in the next section with State.
Level Up 🚀
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
Fully supported.
Fully supported.
Fully supported.
Fully supported.
Accessibility (A11y)
1Descriptive Prop Values Feed Accessible Attributes
When components receive props like `label`, `alt`, or `aria-label`, the parent must actually pass meaningful text rather than empty strings or placeholders — an `<img alt={props.alt} />` is only as accessible as the value supplied by the caller.
2Callback Props Should Preserve Keyboard and Focus Behavior
When a wrapper component forwards an `onClick` callback prop onto a native element, make sure the resulting button or link stays reachable and operable via keyboard, not just mouse clicks.
SEO Implications
- 1
Prop-Driven Content Still Needs to Render Server-Side
If a page's visible text depends on a prop value that isn't available until after a client-side fetch, that content may be missing from the initial server-rendered HTML a crawler sees — pass real content as props at render time whenever it matters for SEO.
- 2
Reusable Prop-Driven Components Simplify Consistent Markup
Building content sections as prop-driven components, such as a single `<ArticleCard title={} excerpt={} />`, makes it easier to guarantee every instance across the site uses the same semantic markup, which helps crawlers parse repeated content consistently.
Best Practices
Destructure Props in the Function Signature
Destructuring in the parameter list, like `function User({ name, age })`, documents exactly which props a component expects and avoids repetitive `props.x` access throughout the function body.
Never Mutate a Prop Inside the Child Component
Props are read-only; if a value needs to change, that change belongs in the parent's state, which then passes a new prop down — reassigning or mutating a prop directly will throw an error or silently fail to update the UI.
Frequent Bugs
A child component tries to update a value it received as a prop and either crashes or has no visible effect.
Props are immutable from the child's perspective. Move the value into the parent's state, pass a callback prop down to the child, and have the child call that callback instead of mutating the prop directly.
A component crashes or renders incorrectly when a parent forgets to pass an optional prop.
Add a default value using ES6 default parameter syntax in the destructuring assignment, e.g. `function Button({ color = 'blue' })`, so the component has a safe fallback when the prop is omitted.
Real-World Examples
Reusable Card Component Driven by Props
A dashboard renders several `<Card title="Revenue" value="$10k" />` and `<Card title="Users" value="452" />` instances from the same component definition, with each instance's displayed content determined entirely by the props it receives.
function Card({ title, value }) {
return (
<div className="card">
<h4>{title}</h4>
<p>{value}</p>
</div>
);
}