Skip to main content

Props: Passing Data to React Components Guide

Props are the fundamental mechanism for passing data from parent to child components in React. This unidirectional data flow makes applications predictable and easier to debug. Learn how to pass different data types, destructure props cleanly, set defaults, and use the special children prop to create flexible wrapper components.

Key Takeaways

  • Props enable one-way data flow: parent to child only, making applications easier to reason about and debug
  • Destructure props in function parameters for clean, readable syntax
  • Default values ensure components work when optional props aren't provided
  • The special children prop lets you pass JSX as a prop, enabling reusable layout components

What Are Props and Why Is Unidirectional Data Flow Important?

Props (short for "properties") are the pipes through which data flows in a React application—strictly one-way: from parent to child. A parent component can pass data to its child via props, but a child cannot modify those props or send data back up through props. Props are read-only.

Think of it like function arguments: a function receives arguments, uses them to perform work, and returns a result—it never changes the original arguments. Similarly, a React component receives props, uses them to render UI, and that's it.

This unidirectional flow has profound benefits: your application is easier to understand (you always know where data comes from), simpler to debug (follow the data down the tree), and more predictable (components can't unexpectedly modify shared state). Without this constraint, components could modify props from parents, creating tangled dependencies and hard-to-trace bugs.

How Do You Pass and Read Props?

Step 1: Passing Props from the Parent Component

Passing props looks identical to passing HTML attributes—you add them as attributes to your custom component's JSX tag.

Create a UserProfile component and pass it data from its parent, App:

// App.jsx
import React from 'react';
import UserProfile from './UserProfile';

function App() {
const userInfo = {
name: 'Hedy Lamarr',
imageUrl: 'https://i.imgur.com/yXOvdOSs.jpg',
profession: 'Inventor & Actress'
};

return (
<div>
<h1>Famous Scientists</h1>
<UserProfile
user={userInfo}
size={100}
/>
</div>
);
}

Here you pass two props to <UserProfile>:

  • user: A JavaScript object containing user information.
  • size: A number.

Step 2: Reading Props in the Child Component

The child component receives all its props as a single object. The modern approach is to destructure this object directly in the function's parameter list:

// UserProfile.jsx
import React from 'react';

function UserProfile({ user, size }) {
return (
<div className="profile-card">
<h2>{user.name}</h2>
<img
className="avatar"
src={user.imageUrl}
alt={user.name}
width={size}
height={size}
/>
<p>Profession: {user.profession}</p>
</div>
);
}

export default UserProfile;

Code Breakdown:

  1. function UserProfile({ user, size }): Instead of receiving a single props object, destructuring directly in the parameter list pulls user and size out and makes them available as local variables.
  2. Using Props: You can now reference user and size directly in JSX, just like any other variable.

How Do You Set Default Values and Use the children Prop?

Specifying Default Prop Values

If a parent doesn't pass a certain prop, it defaults to undefined. You can provide a fallback value directly in destructuring:

function UserProfile({ user, size = 80 }) {
// If size isn't passed, it defaults to 80
}

The default is only used if the prop is missing or explicitly undefined. Passing size={null} or size={0} does not trigger the default.

The Special children Prop

There's one prop that isn't passed like a regular attribute: children. This contains any JSX you nest inside a component's opening and closing tags. It's powerful for creating reusable layout components:

// Card.jsx
import React from 'react';

function Card({ children }) {
const cardStyle = {
border: '1px solid #ccc',
borderRadius: '8px',
padding: '16px',
boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
};

return (
<div style={cardStyle}>
{children}
</div>
);
}

export default Card;

Use it to wrap any content:

// App.jsx
function App() {
return (
<div>
<h1>Famous Scientists</h1>
<Card>
<UserProfile
user={userInfo}
size={100}
/>
</Card>
</div>
);
}

The Card component doesn't need to know what's inside—it just provides a styled wrapper, and children acts as a "slot" the parent fills with any JSX. This pattern is essential for reusable layout components and makes components far more flexible.

Best Practices for Passing and Using Props

Do:

  • Destructure in the parameter list for clean, readable code
  • Provide meaningful default values for optional props
  • Use the children prop for flexible layout components
  • Pass simple, serializable data (objects, arrays, strings, numbers) via props
  • Pass functions as callbacks when a child needs to communicate back to a parent

Avoid:

  • Passing too many props (if a component has 8+ props, consider a different structure)
  • Nested object props without flattening (deeply nested props are hard to track and update)
  • Prop drilling (passing props through intermediate components that don't use them—consider composition instead)
  • Modifying props inside the component (treat them as read-only)

Frequently Asked Questions

Can a child component modify the props it receives?

No. Props are read-only. If you try to assign to a prop (user.name = "Bob"), you'll either get an error (in strict mode) or the change won't trigger a re-render. To update data, use state in the parent component and pass a callback function to the child, or implement state management (Context, Redux) for shared data.

What is the difference between props and state?

Props are read-only data passed from a parent; state is mutable data owned by a component. Props are for parent-to-child communication; state is for component-internal data changes. When props change, the component re-renders; when state changes, React schedules a re-render.

How do I pass multiple props without repeating the component name?

Use the spread operator (...) to pass an object's properties as individual props:

const userProps = { name: 'Alice', size: 100 };
<UserProfile {...userProps} />
// Equivalent to: <UserProfile name="Alice" size={100} />

Be careful: spreading large objects can make it hard to see which props are being used.

Can I pass JSX as a prop other than via children?

Yes. You can pass JSX to any prop:

<Card header={<h2>Title</h2>} footer={<p>Footer</p>} />

Then access it in the component: function Card({ header, footer }) { return <div>{header}...{footer}</div>; }

What happens if I pass null or undefined as a prop value?

Both are valid prop values. null explicitly represents "no value"; undefined means the prop wasn't passed. You can check for either: if (value != null) { ... }. Default values do not trigger if the prop is explicitly null or undefined.

Conclusion

Props are fundamental to React. Mastering them—destructuring, defaults, and the children prop—unlocks your ability to build flexible, reusable components that pass data predictably through your application.

Challenge Yourself: Create a Button component that accepts text (a string), onClick (a function), and disabled (a boolean with default false). Render a <button> that displays the text, calls onClick when clicked, and is disabled if the disabled prop is true.


Glossary

  • Props (Properties): Read-only data passed from a parent component to a child component to customize rendering and behavior.
  • Unidirectional Data Flow: The principle that data in React flows one-way, from parent to child components via props.
  • Destructuring: A JavaScript feature that unpacks values from objects or arrays into distinct variables, commonly used to extract props in the function signature.
  • children Prop: A special prop containing the JSX nested between a component's opening and closing tags.

Further Reading