Hello! Welcome back to our React journey.
Introduction
In our last lesson, we did a great job organizing our UI by creating separate Header, ProfileCard, and Footer components. However, our ProfileCard is still not very useful because the user data is hard-coded inside it. This means we can only ever show Hedy Lamarr's profile.
Today's learning outcome is to pass data from parent to child components using props. "Props" (short for properties) are the mechanism React uses to allow components to communicate. By mastering props, we'll be able to make our ProfileCard truly reusable, capable of displaying data for any user we give it.
This concept of passing data into a self-contained unit of code is fundamental in programming. Given your experience with Laravel, you can think of it as being similar to passing a data array from a controller to a Blade view. In JavaScript terms, it's almost identical to passing arguments to a function.
From Function Arguments to Component Props
The best way to understand props is to relate them to something you already know well: JavaScript functions. A React functional component is just a special kind of function. So, how do you get data into a function? You pass it as an argument. Props are the React way of doing just that.
To build this mental model, let's watch a short video that draws a direct and powerful analogy between plain JavaScript functions and React components.
The video "React Props are Easy" from Sam Meech-Ward provides an excellent conceptual explanation of props. This segment will help you connect your existing JavaScript knowledge to this new React concept.
Watch from 04:06 to 09:14. Pay close attention to how the presenter first builds a regular JavaScript function that accepts parameters, and then shows how a React component does the exact same thing, just with a different syntax for 'calling' it (JSX).
As the video explained, there are two key steps:
- Passing Props: You pass data to a child component using attributes in the JSX tag, just like you'd add attributes to an HTML tag.
- Reading Props: The child component receives all these attributes bundled into a single JavaScript object, which by convention is called
props.
Refactoring the ProfileCard to Use Props
Now, let's apply this concept to our project. We are going to refactor the ProfileCard so that it receives user data from its parent, App.jsx.
Step 1: Move the Data to the Parent
First, cut the user object from src/ProfileCard.jsx and paste it into src/App.jsx. Your App.jsx should now look something like this:
// src/App.jsx
import Header from './Header';
import ProfileCard from './ProfileCard';
import Footer from './Footer';
function App() {
// The data now lives in the parent component
const user = {
name: 'Hedy Lamarr',
bio: 'Inventor and Actress',
imageUrl: 'https://i.imgur.com/yXOvdOSs.jpg',
imageSize: 90,
};
return (
<>
<Header />
<main>
{/* We will pass the data down from here */}
<ProfileCard />
</main>
<Footer />
</>
);
}
export default App;
At this point, your app will be broken because ProfileCard no longer has the user object. Let's fix that.
Step 2: Pass Props to the Child Component
In App.jsx, pass the user data down to the <ProfileCard /> component. You can pass each piece of data as a separate prop. Notice how passing a JavaScript variable (like user.name) requires curly braces, while a plain string doesn't.
// Inside App.jsx's return statement
<ProfileCard
name={user.name}
bio={user.bio}
imageUrl={user.imageUrl}
imageSize={user.imageSize}
/>
Step 3: Read Props in the Child Component
Now, update src/ProfileCard.jsx to receive and use these props. As we learned, props arrive as an object. The modern and cleanest way to use them is to "destructure" them right in the function's signature. This is a powerful ES6 feature that you wanted to master.
Modify ProfileCard.jsx:
// src/ProfileCard.jsx
// Destructure the incoming props object into variables
function ProfileCard({ name, bio, imageUrl, imageSize }) {
return (
<div className="profile-card">
{/* Use the props variables directly */}
<h1>{name}'s Profile</h1>
<img
src={imageUrl}
alt={'Photo of ' + name}
style={{
width: imageSize,
height: imageSize,
borderRadius: '50%'
}}
/>
<p>{bio}</p>
</div>
);
}
export default ProfileCard;
Your app should now be working again! It looks the same, but the data flow is much better. App owns the data, and ProfileCard is now a reusable component that just displays whatever data it is given.
To prove its reusability, try rendering a second ProfileCard in App.jsx with different data:
// Inside App.jsx's return statement
<main>
<ProfileCard
name={user.name}
bio={user.bio}
imageUrl={user.imageUrl}
imageSize={user.imageSize}
/>
<ProfileCard
name="Grace Hopper"
bio="Computer Scientist & US Navy Admiral"
imageUrl="https://i.imgur.com/Mk3eW3As.jpg"
imageSize={90}
/>
</main>
You should see two different profile cards on your page. This is the power of component-based architecture!
Advanced Prop Techniques
Let's explore a few more useful techniques related to props that you'll encounter frequently. The official React documentation is an excellent resource for this.
The official React documentation, "Passing Props to a Component", provides clear explanations and examples for several key prop-related concepts.
Read the sections titled "Specifying a default value for a prop" and "Passing JSX as children". These cover two important patterns.
Default Values for Props
As the documentation explains, you can provide default values for props in case they aren't passed by the parent. This makes your components more robust. The syntax uses the same destructuring assignment pattern from ES6.
// If imageSize is not passed, it will default to 90
function ProfileCard({ name, bio, imageUrl, imageSize = 90 }) {
// ...
}
Spreading Props from an Object
In our example, all the props we passed came from the user object. When an object's keys have the same names as the props your component expects, you can use the ES6 spread syntax (...) as a convenient shortcut.
// In App.jsx
const user = {
name: 'Hedy Lamarr',
bio: 'Inventor and Actress',
imageUrl: 'https://i.imgur.com/yXOvdOSs.jpg',
imageSize: 90,
};
// This one line...
<ProfileCard {...user} />
// ...is equivalent to this:
<ProfileCard
name={user.name}
bio={user.bio}
imageUrl={user.imageUrl}
imageSize={user.imageSize}
/>
This is another ES6 pattern you wanted to practice, and it's very common in React.
The Special children Prop
The children prop is unique. It's not one you pass as an attribute. Instead, it represents whatever JSX you nest inside your component's opening and closing tags. This is perfect for creating generic "wrapper" or "layout" components.
For example, we could create a Card component that provides a styled border and shadow, and then put anything we want inside it.
// Card.jsx
function Card({ children }) {
return (
<div className="card-wrapper">
{children}
</div>
);
}
// App.jsx
<Card>
{/* This ProfileCard is now the 'children' prop of Card */}
<ProfileCard {...user} />
</Card>
A Final, Crucial Rule: Props are Read-Only
One of the most important rules in React is that props are immutable. A component must never, ever change its own props. It can only read them.
Think of props as a snapshot of data at a single point in time. When the parent's data changes, the parent will re-render and pass down a new set of props to the child. The child doesn't modify its old props; it just gets new ones.
This raises a question: "If a component can't change its props, how do we handle user interactions, like typing in a form or clicking a button?"
The answer to that question is state. We will cover state, the second crucial concept for making components interactive, in an upcoming module. For now, just remember: props flow down, and they are read-only.
Conclusion
Great job today! You've mastered a fundamental concept in React that bridges the gap between static components and dynamic, data-driven UIs.
Key Takeaways:
- Props are used to pass data from parent components to child components.
- They are passed as attributes in JSX and received as a single
propsobject in the child. - Destructuring (
function MyComponent({ prop1, prop2 })) is the modern, clean way to access props. - You can make components more robust with default prop values (
prop1 = 'default'). - The spread syntax (
<MyComponent {...dataObject} />) is a useful shortcut. - The special
childrenprop allows you to create flexible wrapper components. - Most importantly, props are immutable (read-only).
Preview of Next Lesson:
We can now render multiple, unique ProfileCard components by hand. But what if we had a list of 100 users from an API? We wouldn't want to write 100 <ProfileCard /> tags. In the next lesson, we will learn how to render lists of data by mapping over arrays and assigning unique keys, a pattern you'll use constantly in React.