Props
In this chapter, you learn about Props, the mechanism for passing data to components.
What you'll learn in this chapter
- The basic usage of Props
- How to define types
- Optional Props and default values
- How to use children
This chapter uses the project you created in Chapter 3. Start the development server with npm run dev and study while writing code.
What are Props
Props (short for properties) are the mechanism for passing data from a parent component to a child component.
The basics of Props
Receiving Props
function Greeting(props: { name: string }) {
return <h1>Hello, {props.name}!</h1>
}
Passing Props
function App() {
return <Greeting name="Tanaka" />
}
Destructuring
It's common to receive Props with destructuring. *Destructuring is explained in Chapter 2.
function Greeting({ name }: { name: string }) {
return <h1>Hello, {name}!</h1>
}
Why is { name }: string not OK?
This is a point of confusion with type annotations in destructuring.
// This is an error
function Greeting({ name }: string) { }
The type annotation represents "the whole object before destructuring."
What you receive as the argument Type annotation
↓ ↓
{ name } : { name: string }
↑ ↑
Take it out with destructuring The type of the original object
Expanded, it means the same as the following.
function Greeting(props: { name: string }) {
const { name } = props // props is an object, so you can destructure it
return <h1>Hello, {name}!</h1>
}
{ name } is syntax for "taking out of an object," so it's contradictory unless the type is also an object type.
How are JSX Props passed?
The attributes you write in JSX are converted into an object and passed to the function.
// This call...
<Greeting name="Tanaka" />
// ...React converts internally to this
Greeting({ name: "Tanaka" })
In other words, the JSX attributes become the object's keys directly.
<UserCard name="Tanaka" age={25} email="test@example.com" />
// ↓ passed as the following object
// { name: "Tanaka", age: 25, email: "test@example.com" }
Separating type definitions
Defining the type of Props separately makes it more readable.
type GreetingProps = {
name: string
}
function Greeting({ name }: GreetingProps) {
return <h1>Hello, {name}!</h1>
}
Whether to use type or interface
You can define the type of Props with either type or interface, but this book standardizes on type. The reason is that type has a wider range of applications, since you can flexibly combine union types, intersection types, mapped types, and so on later. Either is fine as long as your team is consistent.
Multiple Props
You can pass multiple Props.
type UserCardProps = {
name: string
age: number
email: string
}
function UserCard({ name, age, email }: UserCardProps) {
return (
<div>
<h2>{name}</h2>
<p>Age: {age}</p>
<p>Email: {email}</p>
</div>
)
}
function App() {
return (
<UserCard
name="Taro Tanaka"
age={25}
email="tanaka@example.com"
/>
)
}
export default App;
Let's try it: a component using Props
Create a Greeting component that receives name and age as Props and displays "Hello, 〇〇 (△△ years old)!".
Hint
- Define the type with
type GreetingProps = { name: string; age: number } - Receive
{ name, age }with destructuring - Pass the numeric Prop wrapped in
{}(age={25})
Answer and explanation
import './App.css'
type GreetingProps = {
name: string
age: number
}
function Greeting({ name, age }: GreetingProps) {
return <p>Hello, {name} ({age} years old)!</p>
}
// Usage
function App() {
return <Greeting name="Tanaka" age={25} />
}
export default App
Defining the type of Props separately improves the readability of your code. Pass numeric Props wrapped in {} (age={25}). All non-string values (numbers, booleans, objects, arrays, and so on) must be wrapped in {}.
Optional Props
For Props that aren't required, add ? to make them optional.
type ButtonProps = {
label: string
disabled?: boolean
}
function Button({ label, disabled = false }: ButtonProps) {
return (
<button disabled={disabled}>
{label}
</button>
)
}
// Usage
function App() {
return (
<>
<Button label="Submit" />
<Button label="Submit" disabled />
</>
)
}
export default App;
Default values
You can set default values for optional Props.
type AlertProps = {
message: string
type?: 'info' | 'warning' | 'error'
}
function Alert({ message, type = 'info' }: AlertProps) {
const colors = {
info: 'blue',
warning: 'orange',
error: 'red'
}
return (
<div style={{ color: colors[type] }}>
{message}
</div>
)
}
// Usage
function App() {
return (
<>
<Alert message="This is an info message" />
<Alert message="This is a warning message" type="warning" />
<Alert message="This is an error message" type="error" />
</>
)
}
export default App;
Let's try it: add an optional Prop
Add a language prop to the Greeting component you created earlier. The default is Japanese, and if "en" is passed, it greets in English.
Hint
- Define a union-type optional Prop with
language?: 'ja' | 'en' - Set the default value
= 'ja'in the destructuring - Switch the display by language with an
ifstatement or a ternary operator
Answer and explanation
import './App.css'
type GreetingProps = {
name: string
age: number
language?: 'ja' | 'en'
}
function Greeting({ name, age, language = 'ja' }: GreetingProps) {
if (language === 'en') {
return <p>Hello, {name} ({age} years old)!</p>
}
return <p>Hello, {name} ({age} years old)!</p>
}
// Usage
function App() {
return (
<>
<Greeting name="Tanaka" age={25} />
<Greeting name="Tanaka" age={25} language="en" />
</>
)
}
export default App
Adding ? makes it optional, and you can set a default value. Using a union type ('ja' | 'en') lets you restrict the allowed values, and TypeScript checks the types for you.
children Props
The content written between a component's tags is passed as children.
What is React.ReactNode?
React.ReactNode represents every type that React can render.
// A simplified picture (the actual type definition is a bit more complex)
type ReactNode =
| ReactElement // <Component /> or <div>, etc.
| string // "text"
| number // 123
| boolean // true/false (nothing is displayed)
| null // nothing is displayed
| undefined // nothing is displayed
| ReactNode[] // an array
For the type of children, you normally use React.ReactNode.
type CardProps = {
title: string
children: React.ReactNode
}
function Card({ title, children }: CardProps) {
return (
<div className="card">
<h2>{title}</h2>
<div className="card-body">
{children}
</div>
</div>
)
}
function App() {
return (
<Card title="Announcement">
<p>Today's sale has ended.</p>
<p>We look forward to seeing you again tomorrow!</p>
</Card>
)
}
export default App;
Let's try it: a Card component using children
Create a Card component that receives title and children. Give the card a border and padding, and display the title and contents.
Hint
- Add
children: React.ReactNodeto the type definition - Display the child elements with
{children} - Decorate it with inline styles (
border,padding,borderRadius)
Answer and explanation
import './App.css'
type CardProps = {
title: string
children: React.ReactNode
}
function Card({ title, children }: CardProps) {
return (
<div style={{ border: '1px solid #ccc', padding: '16px', borderRadius: '8px' }}>
<h2>{title}</h2>
<div>{children}</div>
</div>
)
}
// Usage
function App() {
return (
<Card title="Announcement">
<p>We are closed today.</p>
<p>We will be back tomorrow.</p>
</Card>
)
}
export default App
Using children, you can receive the content written between a component's tags. This lets you build flexible, highly reusable layout components. It's handy when creating components whose contents you can change freely, like HTML's <div> or <section>.
Spreading an object
You can expand object Props with spread syntax.
type UserProps = {
name: string
age: number
email: string
}
function UserProfile(props: UserProps) {
return (
<div>
<p>Name: {props.name}</p>
<p>Age: {props.age}</p>
<p>Email: {props.email}</p>
</div>
)
}
function App() {
const user = {
name: 'Tanaka',
age: 30,
email: 'tanaka@example.com'
}
return <UserProfile {...user} />
}
export default App;
Principles of Props
1. Props are read-only
You must not modify Props inside a child component.
// NG: trying to modify Props
function Greeting({ name }: { name: string }) {
name = 'Yamada' // it passes syntactically, but you must not do this
return <h1>Hello, {name}!</h1>
}
This reassignment actually does not cause a TypeScript error (because the destructured name is just a local variable). However, a React component should be a pure function that "returns the same result given the same Props," and rewriting Props breaks this premise. This is based on the "immutable" principle explained in Chapter 2. When you want to change the display, don't rewrite Props; have the parent pass different Props, or use State, which you learn in the next chapter.
2. One-way data flow
Data always flows from parent to child. This makes the flow of data easier to predict.
Function Props
You can also pass functions as Props. This is used frequently for event handlers. *The details of event handling are covered in Chapter 8.
type ButtonProps = {
label: string
onClick: () => void
}
function Button({ label, onClick }: ButtonProps) {
return <button onClick={onClick}>{label}</button>
}
function App() {
const handleClick = () => {
console.log('Clicked')
}
return <Button label="Click" onClick={handleClick} />
}
export default App;
ref can also be passed as a Prop (React 19+)
From React 19, you can receive ref in a function component just like a regular Prop. In earlier React (18 and below), you needed to wrap it with forwardRef, but that effort is now gone.
// React 19+: you can receive ref directly from props
type InputProps = {
label: string
ref?: React.Ref<HTMLInputElement>
}
function LabeledInput({ label, ref }: InputProps) {
return (
<label>
{label}
<input ref={ref} />
</label>
)
}
The usage of useRef and the ref attribute itself is covered in detail in Chapter 12, so here it's enough to grasp just the point that "ref can be treated as a kind of Prop."
Summary
- Props are the mechanism for passing data from parent to child
- Define types with TypeScript and use them safely
- You can set default values for optional Props
- You can pass a component's content with
children - From React 19, you can also receive
refas a Prop (noforwardRefneeded) - Props are read-only, and data is one-way
In the next chapter, you learn about State, which manages a component's internal state.