Skip to main content

Passing Props to a Component

React components use props to communicate with each other. Every parent component can pass some information to its child components by giving them props. Props might remind you of HTML attributes, but you can pass any JavaScript value through them, including objects, arrays, and functions.

  • How to pass props to a component
  • How to read props from a component
  • How to specify default values for props
  • How to pass some JSX to a component
  • How props change over time

Familiar props

Props are the information that you pass to a JSX tag. For example, className, src, alt, width, and height are some of the props you can pass to an <img>:

function Avatar() {
return (
<img
className="avatar"
src="https://i.imgur.com/1bX5QH6.jpg"
alt="Lin Lanying"
width={100}
height={100}
/>
);
}

export default function Profile() {
return (
<Avatar />
);
}
body { min-height: 120px; }
.avatar { margin: 20px; border-radius: 50%; }

The props you can pass to an <img> tag are predefined (ReactDOM conforms to the HTML standard). But you can pass any props to your own components, such as <Avatar>, to customize them. Here's how!

Passing props to a component

In this code, the Profile component isn't passing any props to its child component, Avatar:

export default function Profile() {
return (
<Avatar />
);
}

You can give Avatar some props in two steps.

Step 1: Pass props to the child component

First, pass some props to Avatar. For example, let's pass two props: person (an object), and size (a number):

export default function Profile() {
return (
<Avatar
person={{ name: 'Lin Lanying', imageId: '1bX5QH6' }}
size={100}
/>
);
}

If double curly braces after person= confuse you, recall they're merely an object inside the JSX curlies.

Now you can read these props inside the Avatar component.

Step 2: Read props inside the child component

You can read these props by listing their names person, size separated by the commas inside ({ and }) directly after function Avatar. This lets you use them inside the Avatar code, like you would with a variable.

function Avatar({ person, size }) {
// person and size are available here
}

Add some logic to Avatar that uses the person and size props for rendering, and you're done.

Now you can configure Avatar to render in many different ways with different props. Try tweaking the values!

import { getImageUrl } from './utils.js';

function Avatar({ person, size }) {
return (
<img
className="avatar"
src={getImageUrl(person)}
alt={person.name}
width={size}
height={size}
/>
);
}

export default function Profile() {
return (
<div>
<Avatar
size={100}
person={{
name: 'Katsuko Saruhashi',
imageId: 'YfeOqp2'
}}
/>
<Avatar
size={80}
person={{
name: 'Aklilu Lemma',
imageId: 'OKS67lh'
}}
/>
<Avatar
size={50}
person={{
name: 'Lin Lanying',
imageId: '1bX5QH6'
}}
/>
</div>
);
}
export function getImageUrl(person, size = 's') {
return (
'https://i.imgur.com/' +
person.imageId +
size +
'.jpg'
);
}
body { min-height: 120px; }
.avatar { margin: 10px; border-radius: 50%; }

Props let you think about parent and child components independently. For example, you can change the person or the size props inside Profile without having to think about how Avatar uses them. Similarly, you can change how the Avatar uses these props, without looking at the Profile.

You can think of props like "knobs" that you can adjust. They serve the same role as arguments serve for functions—in fact, props are the only argument to your component! React component functions accept a single argument, a props object:

function Avatar(props) {
let person = props.person;
let size = props.size;
// ...
}

Usually you don't need the whole props object itself, so you destructure it into individual props.

Don't miss the pair of { and } curlies inside of ( and ) when declaring props:

function Avatar({ person, size }) {
// ...
}

This syntax is called "destructuring" and is equivalent to reading properties from a function parameter:

function Avatar(props) {
let person = props.person;
let size = props.size;
// ...
}

Specifying a default value for a prop

If you want to give a prop a default value to fall back on when no value is specified, you can do it with the destructuring by putting = and the default value right after the parameter:

function Avatar({ person, size = 100 }) {
// ...
}

Now, if <Avatar person={...} /> is rendered with no size prop, the size will be set to 100.

The default value is only used if the size prop is missing or if you pass size={undefined}. But if you pass size={null} or size={0}, the default value will not be used.

Forwarding props with the JSX spread syntax

Sometimes, passing props gets very repetitive:

function Profile({ person, size, isSepia, thickBorder }) {
return (
<div className="card">
<Avatar
person={person}
size={size}
isSepia={isSepia}
thickBorder={thickBorder}
/>
</div>
);
}

There's nothing wrong with repetitive code—it can be more legible. But at times you may value conciseness. Some components forward all of their props to their children, like how this Profile does with Avatar. Because they don't use any of their props directly, it can make sense to use a more concise "spread" syntax:

function Profile(props) {
return (
<div className="card">
<Avatar {...props} />
</div>
);
}

This forwards all of Profile's props to the Avatar without listing each of their names.

Use spread syntax with restraint. If you're using it in every other component, something is wrong. Often, it indicates that you should split your components and pass children as JSX. More on that next!

Passing JSX as children

It is common to nest built-in browser tags:

<div>
<img />
</div>

Sometimes you'll want to nest your own components the same way:

<Card>
<Avatar />
</Card>

When you nest content inside a JSX tag, the parent component will receive that content in a prop called children. For example, the Card component below will receive a children prop set to <Avatar /> and render it in a wrapper div:

import Avatar from './Avatar.js';

function Card({ children }) {
return (
<div className="card">
{children}
</div>
);
}

export default function Profile() {
return (
<Card>
<Avatar
size={100}
person={{
name: 'Katsuko Saruhashi',
imageId: 'YfeOqp2'
}}
/>
</Card>
);
}
import { getImageUrl } from './utils.js';

export default function Avatar({ person, size }) {
return (
<img
className="avatar"
src={getImageUrl(person)}
alt={person.name}
width={size}
height={size}
/>
);
}
export function getImageUrl(person, size = 's') {
return (
'https://i.imgur.com/' +
person.imageId +
size +
'.jpg'
);
}
.card {
width: fit-content;
margin: 5px;
padding: 5px;
font-size: 20px;
text-align: center;
border: 1px solid #aaa;
border-radius: 20px;
background: #fff;
}
.avatar {
margin: 20px;
border-radius: 50%;
}

Try replacing the <Avatar> inside <Card> with some text to see how the Card component can wrap any nested content. It doesn't need to "know" what's being rendered inside of it. You will see this flexible pattern in many places.

You can think of a component with a children prop as having a "hole" that can be "filled in" by its parent components with arbitrary JSX. You will often use the children prop for visual wrappers: panels, grids, etc.

< src="/images/docs/s/i_children-prop.png" alt='A puzzle-like Card tile with a slot for "children" pieces like text and Avatar' />

How props change over time

The Clock component below receives two props from its parent component: color and time. (The parent component's code is omitted because it uses state, which we won't dive into just yet.)

Try changing the color in the select box below:

export default function Clock({ color, time }) {
return (
<h1 style={{ color: color }}>
{time}
</h1>
);
}
import { useState, useEffect } from 'react';
import Clock from './Clock.js';

function useTime() {
const [time, setTime] = useState(() => new Date());
useEffect(() => {
const id = setInterval(() => {
setTime(new Date());
}, 1000);
return () => clearInterval(id);
}, []);
return time;
}

export default function App() {
const time = useTime();
const [color, setColor] = useState('lightcoral');
return (
<div>
<p>
Pick a color:{' '}
<select value={color} onChange={e => setColor(e.target.value)}>
<option value="lightcoral">lightcoral</option>
<option value="midnightblue">midnightblue</option>
<option value="rebeccapurple">rebeccapurple</option>
</select>
</p>
<Clock color={color} time={time.toLocaleTimeString()} />
</div>
);
}

This example illustrates that a component may receive different props over time. Props are not always static! Here, the time prop changes every second, and the color prop changes when you select another color. Props reflect a component's data at any point in time, rather than only in the beginning.

However, props are immutable—a term from computer science meaning "unchangeable". When a component needs to change its props (for example, in response to a user interaction or new data), it will have to "ask" its parent component to pass it different props—a new object! Its old props will then be cast aside, and eventually the JavaScript engine will reclaim the memory taken by them.

Don't try to "change props". When you need to respond to the user input (like changing the selected color), you will need to "set state", which you can learn about in State: A Component's Memory.

  • To pass props, add them to the JSX, just like you would with HTML attributes.
  • To read props, use the function Avatar({ person, size }) destructuring syntax.
  • You can specify a default value like size = 100, which is used for missing and undefined props.
  • You can forward all props with <Avatar {...props} /> JSX spread syntax, but don't overuse it!
  • Nested JSX like <Card><Avatar /></Card> will appear as Card component's children prop.
  • Props are read-only snapshots in time: every render receives a new version of props.
  • You can't change props. When you need interactivity, you'll need to set state.

Extract a component

This Gallery component contains some very similar markup for two profiles. Extract a Profile component out of it to reduce the duplication. You'll need to choose what props to pass to it.

import { getImageUrl } from './utils.js';

export default function Gallery() {
return (
<div>
<h1>Notable Scientists</h1>
<section className="profile">
<h2>Maria Skłodowska-Curie</h2>
<img
className="avatar"
src={getImageUrl('szV5sdG')}
alt="Maria Skłodowska-Curie"
width={70}
height={70}
/>
<ul>
<li>
<b>Profession: </b>
physicist and chemist
</li>
<li>
<b>Awards: 4 </b>
(Nobel Prize in Physics, Nobel Prize in Chemistry, Davy Medal, Matteucci Medal)
</li>
<li>
<b>Discovered: </b>
polonium (chemical element)
</li>
</ul>
</section>
<section className="profile">
<h2>Katsuko Saruhashi</h2>
<img
className="avatar"
src={getImageUrl('YfeOqp2')}
alt="Katsuko Saruhashi"
width={70}
height={70}
/>
<ul>
<li>
<b>Profession: </b>
geochemist
</li>
<li>
<b>Awards: 2 </b>
(Miyake Prize for geochemistry, Tanaka Prize)
</li>
<li>
<b>Discovered: </b>
a method for measuring carbon dioxide in seawater
</li>
</ul>
</section>
</div>
);
}
export function getImageUrl(imageId, size = 's') {
return (
'https://i.imgur.com/' +
imageId +
size +
'.jpg'
);
}
.avatar { margin: 5px; border-radius: 50%; min-height: 70px; }
.profile {
border: 1px solid #aaa;
border-radius: 6px;
margin-top: 20px;
padding: 10px;
}
h1, h2 { margin: 5px; }
h1 { margin-bottom: 10px; }
ul { padding: 0px 10px 0px 20px; }
li { margin: 5px; }

Start by extracting the markup for one of the scientists. Then find the pieces that don't match it in the second example, and make them configurable by props.

In this solution, the Profile component accepts multiple props: imageId (a string), name (a string), profession (a string), awards (an array of strings), discovery (a string), and imageSize (a number).

Note that the imageSize prop has a default value, which is why we don't pass it to the component.

import { getImageUrl } from './utils.js';

function Profile({
imageId,
name,
profession,
awards,
discovery,
imageSize = 70
}) {
return (
<section className="profile">
<h2>{name}</h2>
<img
className="avatar"
src={getImageUrl(imageId)}
alt={name}
width={imageSize}
height={imageSize}
/>
<ul>
<li><b>Profession:</b> {profession}</li>
<li>
<b>Awards: {awards.length} </b>
({awards.join(', ')})
</li>
<li>
<b>Discovered: </b>
{discovery}
</li>
</ul>
</section>
);
}

export default function Gallery() {
return (
<div>
<h1>Notable Scientists</h1>
<Profile
imageId="szV5sdG"
name="Maria Skłodowska-Curie"
profession="physicist and chemist"
discovery="polonium (chemical element)"
awards={[
'Nobel Prize in Physics',
'Nobel Prize in Chemistry',
'Davy Medal',
'Matteucci Medal'
]}
/>
<Profile
imageId='YfeOqp2'
name='Katsuko Saruhashi'
profession='geochemist'
discovery="a method for measuring carbon dioxide in seawater"
awards={[
'Miyake Prize for geochemistry',
'Tanaka Prize'
]}
/>
</div>
);
}
export function getImageUrl(imageId, size = 's') {
return (
'https://i.imgur.com/' +
imageId +
size +
'.jpg'
);
}
.avatar { margin: 5px; border-radius: 50%; min-height: 70px; }
.profile {
border: 1px solid #aaa;
border-radius: 6px;
margin-top: 20px;
padding: 10px;
}
h1, h2 { margin: 5px; }
h1 { margin-bottom: 10px; }
ul { padding: 0px 10px 0px 20px; }
li { margin: 5px; }

Note how you don't need a separate awardCount prop if awards is an array. Then you can use awards.length to count the number of awards. Remember that props can take any values, and that includes arrays too!

Another solution, which is more similar to the earlier examples on this page, is to group all information about a person in a single object, and pass that object as one prop:

import { getImageUrl } from './utils.js';

function Profile({ person, imageSize = 70 }) {
const imageSrc = getImageUrl(person)

return (
<section className="profile">
<h2>{person.name}</h2>
<img
className="avatar"
src={imageSrc}
alt={person.name}
width={imageSize}
height={imageSize}
/>
<ul>
<li>
<b>Profession:</b> {person.profession}
</li>
<li>
<b>Awards: {person.awards.length} </b>
({person.awards.join(', ')})
</li>
<li>
<b>Discovered: </b>
{person.discovery}
</li>
</ul>
</section>
)
}

export default function Gallery() {
return (
<div>
<h1>Notable Scientists</h1>
<Profile person={{
imageId: 'szV5sdG',
name: 'Maria Skłodowska-Curie',
profession: 'physicist and chemist',
discovery: 'polonium (chemical element)',
awards: [
'Nobel Prize in Physics',
'Nobel Prize in Chemistry',
'Davy Medal',
'Matteucci Medal'
],
}} />
<Profile person={{
imageId: 'YfeOqp2',
name: 'Katsuko Saruhashi',
profession: 'geochemist',
discovery: 'a method for measuring carbon dioxide in seawater',
awards: [
'Miyake Prize for geochemistry',
'Tanaka Prize'
],
}} />
</div>
);
}
export function getImageUrl(person, size = 's') {
return (
'https://i.imgur.com/' +
person.imageId +
size +
'.jpg'
);
}
.avatar { margin: 5px; border-radius: 50%; min-height: 70px; }
.profile {
border: 1px solid #aaa;
border-radius: 6px;
margin-top: 20px;
padding: 10px;
}
h1, h2 { margin: 5px; }
h1 { margin-bottom: 10px; }
ul { padding: 0px 10px 0px 20px; }
li { margin: 5px; }

Although the syntax looks slightly different because you're describing properties of a JavaScript object rather than a collection of JSX attributes, these examples are mostly equivalent, and you can pick either approach.

Adjust the image size based on a prop

In this example, Avatar receives a numeric size prop which determines the <img> width and height. The size prop is set to 40 in this example. However, if you open the image in a new tab, you'll notice that the image itself is larger (160 pixels). The real image size is determined by which thumbnail size you're requesting.

Change the Avatar component to request the closest image size based on the size prop. Specifically, if the size is less than 90, pass 's' ("small") rather than 'b' ("big") to the getImageUrl function. Verify that your changes work by rendering avatars with different values of the size prop and opening images in a new tab.

import { getImageUrl } from './utils.js';

function Avatar({ person, size }) {
return (
<img
className="avatar"
src={getImageUrl(person, 'b')}
alt={person.name}
width={size}
height={size}
/>
);
}

export default function Profile() {
return (
<Avatar
size={40}
person={{
name: 'Gregorio Y. Zara',
imageId: '7vQD0fP'
}}
/>
);
}
export function getImageUrl(person, size) {
return (
'https://i.imgur.com/' +
person.imageId +
size +
'.jpg'
);
}
.avatar { margin: 20px; border-radius: 50%; }

Here is how you could go about it:

import { getImageUrl } from './utils.js';

function Avatar({ person, size }) {
let thumbnailSize = 's';
if (size > 90) {
thumbnailSize = 'b';
}
return (
<img
className="avatar"
src={getImageUrl(person, thumbnailSize)}
alt={person.name}
width={size}
height={size}
/>
);
}

export default function Profile() {
return (
<>
<Avatar
size={40}
person={{
name: 'Gregorio Y. Zara',
imageId: '7vQD0fP'
}}
/>
<Avatar
size={120}
person={{
name: 'Gregorio Y. Zara',
imageId: '7vQD0fP'
}}
/>
</>
);
}
export function getImageUrl(person, size) {
return (
'https://i.imgur.com/' +
person.imageId +
size +
'.jpg'
);
}
.avatar { margin: 20px; border-radius: 50%; }

You could also show a sharper image for high DPI screens by taking window.devicePixelRatio into account:

import { getImageUrl } from './utils.js';

const ratio = window.devicePixelRatio;

function Avatar({ person, size }) {
let thumbnailSize = 's';
if (size * ratio > 90) {
thumbnailSize = 'b';
}
return (
<img
className="avatar"
src={getImageUrl(person, thumbnailSize)}
alt={person.name}
width={size}
height={size}
/>
);
}

export default function Profile() {
return (
<>
<Avatar
size={40}
person={{
name: 'Gregorio Y. Zara',
imageId: '7vQD0fP'
}}
/>
<Avatar
size={70}
person={{
name: 'Gregorio Y. Zara',
imageId: '7vQD0fP'
}}
/>
<Avatar
size={120}
person={{
name: 'Gregorio Y. Zara',
imageId: '7vQD0fP'
}}
/>
</>
);
}
export function getImageUrl(person, size) {
return (
'https://i.imgur.com/' +
person.imageId +
size +
'.jpg'
);
}
.avatar { margin: 20px; border-radius: 50%; }

Props let you encapsulate logic like this inside the Avatar component (and change it later if needed) so that everyone can use the <Avatar> component without thinking about how the images are requested and resized.

Passing JSX in a children prop

Extract a Card component from the markup below, and use the children prop to pass different JSX to it:

export default function Profile() {
return (
<div>
<div className="card">
<div className="card-content">
<h1>Photo</h1>
<img
className="avatar"
src="https://i.imgur.com/OKS67lhm.jpg"
alt="Aklilu Lemma"
width={70}
height={70}
/>
</div>
</div>
<div className="card">
<div className="card-content">
<h1>About</h1>
<p>Aklilu Lemma was a distinguished Ethiopian scientist who discovered a natural treatment to schistosomiasis.</p>
</div>
</div>
</div>
);
}
.card {
width: fit-content;
margin: 20px;
padding: 20px;
border: 1px solid #aaa;
border-radius: 20px;
background: #fff;
}
.card-content {
text-align: center;
}
.avatar {
margin: 10px;
border-radius: 50%;
}
h1 {
margin: 5px;
padding: 0;
font-size: 24px;
}

Any JSX you put inside of a component's tag will be passed as the children prop to that component.

This is how you can use the Card component in both places:

function Card({ children }) {
return (
<div className="card">
<div className="card-content">
{children}
</div>
</div>
);
}

export default function Profile() {
return (
<div>
<Card>
<h1>Photo</h1>
<img
className="avatar"
src="https://i.imgur.com/OKS67lhm.jpg"
alt="Aklilu Lemma"
width={100}
height={100}
/>
</Card>
<Card>
<h1>About</h1>
<p>Aklilu Lemma was a distinguished Ethiopian scientist who discovered a natural treatment to schistosomiasis.</p>
</Card>
</div>
);
}
.card {
width: fit-content;
margin: 20px;
padding: 20px;
border: 1px solid #aaa;
border-radius: 20px;
background: #fff;
}
.card-content {
text-align: center;
}
.avatar {
margin: 10px;
border-radius: 50%;
}
h1 {
margin: 5px;
padding: 0;
font-size: 24px;
}

You can also make title a separate prop if you want every Card to always have a title:

function Card({ children, title }) {
return (
<div className="card">
<div className="card-content">
<h1>{title}</h1>
{children}
</div>
</div>
);
}

export default function Profile() {
return (
<div>
<Card title="Photo">
<img
className="avatar"
src="https://i.imgur.com/OKS67lhm.jpg"
alt="Aklilu Lemma"
width={100}
height={100}
/>
</Card>
<Card title="About">
<p>Aklilu Lemma was a distinguished Ethiopian scientist who discovered a natural treatment to schistosomiasis.</p>
</Card>
</div>
);
}
.card {
width: fit-content;
margin: 20px;
padding: 20px;
border: 1px solid #aaa;
border-radius: 20px;
background: #fff;
}
.card-content {
text-align: center;
}
.avatar {
margin: 10px;
border-radius: 50%;
}
h1 {
margin: 5px;
padding: 0;
font-size: 24px;
}