Margherita
Small · Quantity: 3
Vegetarian
Nikita Goluban
Full-Stack Developer Logistics & Marketing Mechanical & Construction Skills Woodwork & Metalwork Polyglot
React.js + Next.js · Module 2. React Basics · Lesson 5
Props (short for properties) allow React components to receive data from other components.
Props make components flexible and reusable.
Without props, a component with fixed content always displays the same information.
With props, the same component can display different pizza names, sizes, prices, availability states, images, and other information.
Throughout this lesson, we will model the reusable pizza cards and controls needed for an online pizza-ordering website.
Props are one of the fundamental concepts of React.
REACT · 01
Props are values passed from a parent component to a child component.
They work similarly to arguments passed to JavaScript functions.
function greet(name) {
console.log("Hello " + name);
}
greet("Nikita");
greet("Alex");
The same function works with different arguments.
React components work in a similar way.
function Greeting(props: { name: string }) {
return <h1>Hello {props.name}!</h1>;
}
export default function App() {
return (
<div>
<Greeting name="Nikita" />
<Greeting name="Alex" />
</div>
);
}
name prop.Greeting is a reusable component.
name="Nikita" passes a value to the component.
props.name reads the value.
The same component displays different information depending on the props it receives.
REACT · 02
In React, components are organized into a component tree.
A parent component can pass information to its child components.
function PizzaCard(props: { name: string }) {
return <h2>{props.name}</h2>;
}
export default function App() {
return (
<div>
<PizzaCard name="Margherita" />
</div>
);
}
Here:
App is the parent component.PizzaCard is the child component.name is the prop."Margherita" is the value passed through the prop.App
|
| name="Margherita"
|
v
PizzaCard
|
| props.name
|
v
Margherita
Important: Props normally flow from parent components to child components.
This is called one-way data flow.
REACT · 03
React automatically groups the attributes passed to a component into an object.
For example:
<PizzaCard name="Margherita" quantity={3} />
React provides a props object conceptually equivalent to:
{
name: "Margherita",
quantity: 3
}
Inside the component, we can access these values using dot notation:
props.name
props.quantity
interface PizzaCardProps {
name: string;
quantity: number;
}
function PizzaCard(props: PizzaCardProps) {
return (
<div>
<h2>{props.name}</h2>
<p>Quantity: {props.quantity}</p>
</div>
);
}
export default PizzaCard;
props is the object received by the component.
props.name accesses the pizza name.
props.quantity accesses the number of pizzas.
This is ordinary JavaScript object property access.
REACT · 04
In TypeScript, we should define the expected structure of component props.
We can use an interface or a type.
In this course, we will primarily use interface.
interface PizzaCardProps {
name: string;
}
This interface describes an object with one property:
name: string;
The property must contain a string.
interface PizzaCardProps {
name: string;
}
function PizzaCard(props: PizzaCardProps) {
return (
<h2>{props.name}</h2>
);
}
export default PizzaCard;
props: PizzaCardProps
means that the props parameter must follow the structure defined by PizzaCardProps.
TypeScript checks whether the correct values are provided.
<PizzaCard name="Margherita" />
<PizzaCard name={2048} />
This produces a TypeScript error because name must be a string, not a number.
REACT · 05
Props are passed using syntax similar to HTML attributes.
<PizzaCard size="Small" />
The prop name is size.
The prop value is "Small".
Inside the component:
props.size
returns the string "Small".
interface PizzaCardProps {
size: string;
}
function PizzaCard(props: PizzaCardProps) {
return (
<div>
<h2>{props.size}</h2>
</div>
);
}
export default function App() {
return (
<div>
<PizzaCard size="Small" />
<PizzaCard size="Medium" />
<PizzaCard size="Large" />
</div>
);
}
One component displays three different sizes.
REACT · 06
A component can receive several props.
For example, a pizza card might need:
interface PizzaCardProps {
name: string;
size: string;
imageUrl: string;
}
interface PizzaCardProps {
name: string;
size: string;
imageUrl: string;
}
function PizzaCard(props: PizzaCardProps) {
return (
<div>
<h2>{props.name}</h2>
<img
src={props.imageUrl}
alt={`Pizza ${props.name}`}
width={320}
/>
<p>Size: {props.size}</p>
</div>
);
}
export default PizzaCard;
import PizzaCard from "./PizzaCard";
export default function App() {
return (
<div>
<PizzaCard
name="Margherita"
size="Small"
imageUrl="/images/margherita-pizza.jpg"
/>
<PizzaCard
name="Pepperoni"
size="Medium"
imageUrl="/images/pepperoni-pizza.jpg"
/>
</div>
);
}
The same PizzaCard component receives different data.
React renders each component using the values passed through props.
The image paths in this example assume that the files are stored inside Vite's public/images/ directory.
REACT · 07
Props can contain many kinds of JavaScript values.
<PizzaCard name="Margherita" />
String literals are normally written inside quotation marks.
<PizzaCard quantity={3} />
Numbers are passed inside curly braces.
<PizzaCard isVegetarian={true} />
Boolean values are also passed inside curly braces.
const quantity = 3;
<PizzaCard quantity={quantity} />
The variable is evaluated inside {}.
<PizzaCard quantity={1 + 2} />
React receives the result of the expression.
interface PizzaCardProps {
name: string;
quantity: number;
isVegetarian: boolean;
}
function PizzaCard(props: PizzaCardProps) {
return (
<div>
<h2>{props.name}</h2>
<p>Quantity: {props.quantity}</p>
<p>
{props.isVegetarian ? "Vegetarian" : "Contains meat"}
</p>
</div>
);
}
export default PizzaCard;
Usage:
<PizzaCard
name="Margherita"
quantity={3}
isVegetarian={true}
/>
REACT · 08
By default, properties declared in a TypeScript interface are required.
interface PizzaCardProps {
name: string;
specialInstructions: string;
}
Both name and specialInstructions must be provided.
Add ? after the property name:
interface PizzaCardProps {
name: string;
specialInstructions?: string;
}
Now specialInstructions is optional.
interface PizzaCardProps {
name: string;
specialInstructions?: string;
}
function PizzaCard(props: PizzaCardProps) {
return (
<div>
<h2>{props.name}</h2>
<p>
Special instructions: {props.specialInstructions ?? "None"}
</p>
</div>
);
}
export default PizzaCard;
Usage:
<PizzaCard name="Margherita" specialInstructions="Extra basil" />
<PizzaCard name="Pepperoni" />
The ? means the prop can be omitted.
When omitted, props.specialInstructions is undefined.
The nullish coalescing operator ?? uses a fallback value when the left-hand value is null or undefined.
REACT · 09
For beginners, accessing props directly through the props object can be easier to understand.
interface PizzaCardProps {
name: string;
quantity: number;
}
function PizzaCard(props: PizzaCardProps) {
return (
<div>
<h2>{props.name}</h2>
<p>Quantity: {props.quantity}</p>
</div>
);
}
Here, every value clearly comes from the props object.
props.name
props.quantity
This is the approach used in most examples in these Dev Notes.
REACT · 10
Many React tutorials use destructuring.
function PizzaCard(props: PizzaCardProps) {
return <h2>{props.name}</h2>;
}
function PizzaCard({ name }: PizzaCardProps) {
return <h2>{name}</h2>;
}
Both versions produce the same result.
Destructuring is a JavaScript feature that extracts properties from an object.
Example:
const pizza = {
name: "Margherita",
quantity: 3
};
const name = pizza.name;
This can also be written as:
const { name } = pizza;
In React, destructuring is optional.
For learning purposes, using props.name makes the data flow more explicit.
REACT · 11
Props should be treated as immutable values.
A child component must not directly modify the props it receives.
interface PizzaCardProps {
quantity: number;
}
function PizzaCard(props: PizzaCardProps) {
props.quantity = 5;
return <p>{props.quantity}</p>;
}
This violates React's rules.
interface PizzaCardProps {
quantity: number;
}
function PizzaCard(props: PizzaCardProps) {
return <p>Quantity: {props.quantity}</p>;
}
If a value needs to change, the component that owns the data should update it.
Later, we will learn how React state and callback props work together.
Important: React expects props to be treated as read-only, including objects and arrays passed through props.
REACT · 12
Props can also contain functions.
This allows a parent component to provide behavior to a child component.
Create Button.tsx:
interface ButtonProps {
label: string;
onPress: () => void;
}
function Button(props: ButtonProps) {
return (
<button onClick={props.onPress}>
{props.label}
</button>
);
}
export default Button;
import Button from "./Button";
export default function App() {
function handleAddToCart() {
console.log("Pizza added to cart!");
}
return (
<Button
label="Add to cart"
onPress={handleAddToCart}
/>
);
}
label contains the button text.
onPress contains a function.
props.onPress passes that function to the button's onClick.
When the user clicks the button, React executes the function provided by the parent.
onPress: () => void;
This is TypeScript syntax describing a function type.
It means that onPress must be a function that requires no arguments and returns no useful value.
The => in this case is part of a type declaration, not an arrow function implementation.
REACT · 13
Props and state are related but serve different purposes.
Quick rule: use props for information a component receives. Use state for information a component owns and needs to change over time.
| Question | Props | State |
|---|---|---|
| Where does the value come from? | A parent component | The component that owns the value |
| Can this component change it directly? | No — props are read-only | Yes — through a state setter |
| How is it provided? | Through JSX attributes | With a hook such as useState |
| What causes a new value? | The parent renders with new props | User actions, API results, timers, or other events |
| Pizza-ordering example | Pizza name or unit price | Selected quantity or open/closed cart details |
Use props when:
Examples include a pizza name, size, price, button label, image URL, or onConfirm function.
Use state when:
Examples include a counter, selected tab, form input, modal visibility, loading status, or fetched data.
Ask these questions in order:
The same value can therefore be state in a parent and a prop in a child. These terms describe how each component uses the value, not the type of the value itself.
import { useState } from "react";
interface PizzaCardProps {
name: string;
unitPrice: number;
}
function PizzaCard(props: PizzaCardProps) {
const state = useState(1);
const quantity = state[0];
const setQuantity = state[1];
function addPizza() {
setQuantity(quantity + 1);
}
return (
<div>
<h2>{props.name}</h2>
<p>Price per pizza: {props.unitPrice} €</p>
<p>Quantity: {quantity}</p>
<p>Total: {props.unitPrice * quantity} €</p>
<button onClick={addPizza}>
Add pizza
</button>
</div>
);
}
export default PizzaCard;
name and unitPrice are received through props.
quantity is stored in local state.
total does not need separate state because it can be calculated from props.unitPrice and quantity during every render.
The component can update its own state, but it must not directly modify its props.
Avoid copying a prop into state unless the component intentionally needs an independent editable value.
// Usually unnecessary: two places now store the same information.
const [name, setName] = useState(props.name);
If the component only displays the name, use props.name directly. Keeping one source of truth prevents the prop and state from becoming inconsistent.
REACT · 15
Incorrect:
<PizzaCard />
If name is required, TypeScript reports an error.
Correct:
<PizzaCard name="Margherita" />
Incorrect:
<PizzaCard quantity="3" />
If quantity is defined as number, this is a string.
Correct:
<PizzaCard quantity={3} />
Incorrect:
<PizzaCard quantity="plannedQuantity" />
This passes the literal text "plannedQuantity".
Correct:
<PizzaCard quantity={plannedQuantity} />
Incorrect:
function PizzaCard(props: PizzaCardProps) {
return <h2>{name}</h2>;
}
If name is not defined as a local variable, TypeScript reports an error.
Correct:
function PizzaCard(props: PizzaCardProps) {
return <h2>{props.name}</h2>;
}
Incorrect:
props.size = "Extra large";
Props are read-only. Ask the parent to provide a new value or use component state for data owned by the component.
Incorrect:
<button onClick={props.onPress()}>
Confirm
</button>
Correct:
<button onClick={props.onPress}>
Confirm
</button>
Pass the function itself so React can call it after the click.
REACT · 16
Create a Greeting component with a required name: string prop. Render it twice with different names.
Create a PizzaCard component with name, size, and quantity props. Access every value through the props object.
Add an optional specialInstructions prop and display None when it is missing.
Add a boolean isVegetarian prop. Use a ternary expression to display the correct status.
Create an ActionButton component that receives a label and an onPress function. Let App provide the click handler.
REACT · 17
? marks an optional TypeScript property.REACT · 18
Next, learn how to inspect components, props, state, and renders with React Developer Tools.