← Back to React
Lesson 12 · Components and Props

Props Basics

Passing data from a parent component to a child - the thing that makes components reusable.

Beginner35 min

What you will be able to do

  • Explain what props are, and how a component receives them
  • Pass strings, numbers, booleans, arrays, objects, functions, and JSX as props
  • Tell "192" from {192} - and see what goes wrong when you mix them up
  • Type props with TypeScript, and read the errors it gives
  • Explain one-way data flow, and why a child must never change its props
  • Choose between passing a whole object and passing individual fields

The idea, in plain English

The components in Lesson 11 always showed the same thing: every CourseCard said React. Props fix that. A prop is a value a parent passes to a child, written like an HTML attribute: <CourseCard title="React" />. React collects every attribute into one object - { title: "React" } - and passes it to your component function as its first argument.

So a component with props is just a function with a parameter. The same CourseCard, called with a different title, shows a different course. One component, any number of results.

Props flow one way: from parent to child. The parent owns the data and decides what to pass; the child reads it and renders. A child never changes its props - in development React freezes the props object, and trying to write to it crashes the component.

Worked example: One CourseCard, three different courses.

Passing and receiving

In the parent, props look like attributes: <CourseCard title="React" lessons={192} />. In the child, they arrive as one object - the first parameter of the function. function CourseCard(props) receives { title: "React", lessons: 192 }, and props.title is "React".

Log the props and that is exactly what you see: {"title":"React","lessons":192}. key never appears there - React keeps it for itself (Lesson 9).

Quotes pass strings, braces pass anything

A value in quotes is always a string: title="React". A value in braces is a JavaScript expression, so it can be a number, a boolean, an array, an object, a function, JSX, or a calculation: lessons={192}, lessons={100 + 92}, title={course.name}.

lessons="192" and lessons={192} look almost the same and are not. The first passes the string "192", the second the number 192. Add 1 in the child and you get "1921" from the first and 193 from the second. With the prop typed as number, TypeScript refuses the string: "Type string is not assignable to type number".

A prop written with no value is true: <CourseCard published /> is the same as published={true}. Leave it off and the prop is undefined, which behaves like false in a condition.

Passing each kind of value
Stringtitle="React" - quotes, or title={"React"}
Numberlessons={192} - braces; "192" would be a string
Booleanpublished={true}, or just published
Arraytopics={["JSX", "Props", "State"]}
Objectcourse={{ title: "React", lessons: 192 }} - outer braces for JSX, inner for the object
FunctiononStart={handleStart} - the function itself, not handleStart()
JSXaction={<button>Start</button>}

Typing props with TypeScript

Describe the props object with a type, and annotate the parameter: type CourseCardProps = { title: string; lessons: number }; function CourseCard(props: CourseCardProps). Most code destructures in the same place - function CourseCard({ title, lessons }: CourseCardProps) - which Lesson 13 covers in depth.

Untyped props do not compile in the Vite TypeScript template. function CourseCard(props) fails with "Parameter props implicitly has an any type", because strict mode is on. Every component in this course types its props.

The payoff is at the call site. Forget a required prop, misspell one, or pass the wrong kind of value, and TypeScript underlines the JSX before you ever run it.

Props make one component do the work of many

Without props you would need ReactCourseCard, TypeScriptCourseCard, and NodeCourseCard - three copies of the same markup. With a title prop, one CourseCard covers all three, and every future course too.

Combined with map from Lesson 9, props are how real lists are built: courses.map((course) => <CourseCard key={course.id} course={course} />). The list owns the data; each card receives one item.

A whole object, or individual fields?

<CourseCard course={course} /> passes the whole object. <CourseCard title={course.title} lessons={course.lessons} /> passes only the fields the card uses. Both are normal.

Pass the object when the component represents that thing - a CourseCard shows a course. Pass fields when the component is more general - a Stat that shows any label and number should not need to know what a course is.

Props are read-only

The parent owns the data; the child only reads it. This is one-way data flow, and it is what makes React apps predictable: to find out why a card shows "React", you look up the tree at whoever passed it.

React enforces it in development. props.title = "Angular" throws "Cannot assign to read only property title", and the component fails to render. In a production build the same line runs silently - which is worse, because the bug only shows up in the build you did not test.

Reassigning a destructured variable - title = "Angular" - does not touch the props object, so nothing throws, but it is still a sign of a design problem. If a value needs to change in response to the user, that is state - Module 3.

Watch out: Writing to props crashes in development and silently works in production. Never write to props - not even when the production build seems fine.

Functions and JSX as props

A prop can hold a function: <CourseCard onStart={handleStart} />. The child calls it - usually from an event - and the parent code runs. That is how a child tells its parent something happened, and it is the whole of Lesson 14.

A prop can also hold JSX, typed as ReactNode: <CourseCard action={<button>Start</button>} /> lets the parent decide what goes in a slot inside the card. The special children prop, for content between the opening and closing tags, is Lesson 15.

Syntax and examples

One component, three courses
type CourseCardProps = { title: string; }; function CourseCard(props: CourseCardProps) { return <h2>{props.title}</h2>; } function App() { return ( <div> <CourseCard title="React" /> <CourseCard title="TypeScript" /> <CourseCard title="Node.js" /> </div> ); } // React // TypeScript // Node.js
What the component receives
<CourseCard key={1} title="React" lessons={192} published /> function CourseCard(props: CourseCardProps) { console.log(props); // { title: "React", lessons: 192, published: true } - no key return <h2>{props.title}</h2>; }
"192" is not {192}
function Stats({ lessons }: { lessons: number }) { return <p>{lessons + 1}</p>; } <Stats lessons={192} />; // 193 <Stats lessons="192" />; // TypeScript: Type 'string' is not assignable to type 'number'. // Without types, it renders "1921" - string + 1 joins them.
Every kind of value
import type { ReactNode } from "react"; type CourseCardProps = { title: string; lessons: number; published: boolean; topics: string[]; instructor: { name: string; experience: number }; onStart: () => void; action: ReactNode; }; function CourseCard({ title, lessons, published, topics, instructor, onStart, action }: CourseCardProps) { return ( <article> <h2>{title}</h2> <p>{lessons} lessons</p> {published && <span>Published</span>} <ul> {topics.map((topic) => ( <li key={topic}>{topic}</li> ))} </ul> <p>Instructor: {instructor.name}, {instructor.experience} years</p> <button onClick={onStart}>Start Learning</button> {action} </article> ); } <CourseCard title="React" lessons={192} published topics={["JSX", "Props", "State", "Hooks"]} instructor={{ name: "Chandu", experience: 8 }} onStart={() => console.log("Started")} action={<a href="/resources/react">Syllabus</a>} />;
Props with a list: the pattern you will use everywhere
type Course = { id: number; title: string; level: string; lessons: number; }; const courses: Course[] = [ { id: 1, title: "React", level: "Beginner", lessons: 192 }, { id: 2, title: "TypeScript", level: "Intermediate", lessons: 133 }, { id: 3, title: "Node.js", level: "Intermediate", lessons: 120 }, ]; function CourseCard({ course }: { course: Course }) { return ( <article> <h2>{course.title}</h2> <p>Level: {course.level}</p> <p>Lessons: {course.lessons}</p> </article> ); } function CourseList() { return ( <div> {courses.map((course) => ( <CourseCard key={course.id} course={course} /> ))} </div> ); }
The parent owns the data
function CoursePage() { const courseName = "React"; // owned here return <CourseCard title={courseName} />; } function CourseCard({ title }: { title: string }) { const message = "Start Learning"; // the card's own local value return ( <div> {/* title comes from the parent */} <h2>{title}</h2> {/* message is the card's own */} <button>{message}</button> </div> ); }
Writing to props - checked in React 19
function CourseCard(props: { title: string }) { props.title = "Angular"; return <h2>{props.title}</h2>; } // Development: TypeError: Cannot assign to read only property 'title' of object // - the component fails to render. // Production: renders "Angular" silently.
Props that change behaviour, not just text
type ButtonProps = { label: string; disabled: boolean; }; function Button({ label, disabled }: ButtonProps) { return <button disabled={disabled}>{label}</button>; } <Button label="Start Course" disabled={false} />; <Button label="Coming Soon" disabled={true} />;

Tip: Read a props type like a contract. It lists what the component needs to do its job, and TypeScript holds every caller to it.

Props at a glance

Pass

Attributes on the component tag.

<CourseCard title="React" lessons={192} />
Receive

One object, the first parameter.

function CourseCard(props: Props)
Type

Describe the object with a type.

type Props = { title: string };
String vs number

Quotes pass a string; braces pass the value.

lessons={192}
true shorthand

A bare prop is true.

<CourseCard published />
Read-only

Never assign to props.

// props.title = ... throws in dev

Try it yourself

The code does not change. Swap the content string and the program does something else entirely.

Log the props

“Add console.log(props) to a component, pass it three props and a key, and read what arrives.”

String or number

“Render {lessons + 1} in a component, then pass lessons="192" and lessons={192} (with the type removed). What do you get each time?”

Break the contract

“With a typed CourseCard, leave out a required prop, misspell another, and pass a string for a number. Read each TypeScript error.”

Write to a prop

“Set props.title = "Angular" inside a component and load the page in development. Then run a production build and preview it.”

What usually goes wrong

Quotes around a number

Quotes always pass a string, so arithmetic joins instead of adding.

✗ <CourseCard lessons="192" />
✓ <CourseCard lessons={192} />
Untyped props in a TypeScript project

Strict mode rejects an implicit any parameter.

✗ function CourseCard(props) { ... }
✓ function CourseCard(props: CourseCardProps) { ... }
Changing a prop

Props belong to the parent. React throws in development and silently allows it in production.

✗ props.title = "New Title";
✓ // the parent passes a different title - or the value becomes state
Calling a function prop instead of passing it

handleStart() runs during render and passes its return value. TypeScript reports "Type void is not assignable to type () => void".

✗ <CourseCard onStart={handleStart()} />
✓ <CourseCard onStart={handleStart} />
Single braces around an object literal

An object prop needs two sets of braces: the outer pair switches to JavaScript, the inner pair is the object.

✗ course={ title: "React" }
✓ course={{ title: "React" }}

Best practices

  • Type every component props with a named type, such as CourseCardProps.
  • Use quotes only for fixed strings; everything else goes in braces.
  • Pass the whole object when the component represents it, and fields when it is more general.
  • Treat props as read-only, always.
  • Keep the data in the parent and let children render it.

Practice

Write these yourself before opening anything. Getting them wrong first is most of how this sticks.

1.

Build a LessonCard with title: string, duration: number, and completed: boolean that shows the title, "Duration: 30 minutes", and either "✓ Completed" or "○ Not Completed".

Show hint

Two outcomes for completed - a ternary.

Show solution
type LessonCardProps = { title: string; duration: number; completed: boolean; }; function LessonCard({ title, duration, completed }: LessonCardProps) { return ( <article> <h3>{title}</h3> <p>Duration: {duration} minutes</p> <p>{completed ? "✓ Completed" : "○ Not Completed"}</p> </article> ); } <LessonCard title="JSX Fundamentals" duration={30} completed />; <LessonCard title="Props Basics" duration={25} completed={false} />;
2.

Build a CourseCard with title, instructor, lessons, level, rating, and enrolledStudents, showing enrolled students with thousands separators (12,450). Render three courses with it.

Show hint

toLocaleString formats a number for display.

Show solution
type CourseCardProps = { title: string; instructor: string; lessons: number; level: string; rating: number; enrolledStudents: number; }; function CourseCard({ title, instructor, lessons, level, rating, enrolledStudents }: CourseCardProps) { return ( <article> <h2>{title}</h2> <p>Instructor: {instructor}</p> <p>{lessons} Lessons</p> <p>{level}</p> <p>⭐ {rating}</p> <p>{enrolledStudents.toLocaleString("en-US")} Students</p> </article> ); } function Courses() { return ( <div> <CourseCard title="React" instructor="John" lessons={192} level="Beginner" rating={4.8} enrolledStudents={12450} /> <CourseCard title="TypeScript" instructor="Asha" lessons={133} level="Intermediate" rating={4.7} enrolledStudents={8320} /> <CourseCard title="Node.js" instructor="Ravi" lessons={120} level="Intermediate" rating={4.6} enrolledStudents={6015} /> </div> ); }
3.

Fix the four prop mistakes: <CourseCard title=React lessons="192" course={ title: "React" } onStart={start()} />

Show hint

One missing pair of quotes, one wrong pair of quotes, one missing pair of braces, and one pair of parentheses too many.

Show solution
<CourseCard title="React" lessons={192} course={{ title: "React" }} onStart={start} />
Coding challenge

A course page built from props

Split a course page into three components and pass every piece of data down from one parent.

It should
  • CoursePage owns all the data and renders CourseHeader, CourseStats, and CourseAction.
  • CourseHeader takes title, level, and instructor.
  • CourseStats takes lessons (number), duration (string), and students (number, shown as 12,450).
  • CourseAction takes onStart and renders a Start Learning button that calls it.
  • Every component has a props type.
function CoursePage() { const course = { title: "React", level: "Beginner", instructor: "John", lessons: 192, duration: "24 hours", students: 12450, }; function handleStart() { console.log("Starting", course.title); } // render CourseHeader, CourseStats, CourseAction }
Show one solution
type CourseHeaderProps = { title: string; level: string; instructor: string }; function CourseHeader({ title, level, instructor }: CourseHeaderProps) { return ( <header> <h1>{title}</h1> <p>{level} · Instructor: {instructor}</p> </header> ); } type CourseStatsProps = { lessons: number; duration: string; students: number }; function CourseStats({ lessons, duration, students }: CourseStatsProps) { return ( <ul> <li>{lessons} lessons</li> <li>{duration}</li> <li>{students.toLocaleString("en-US")} students</li> </ul> ); } type CourseActionProps = { onStart: () => void }; function CourseAction({ onStart }: CourseActionProps) { return <button onClick={onStart}>Start Learning</button>; } function CoursePage() { const course = { title: "React", level: "Beginner", instructor: "John", lessons: 192, duration: "24 hours", students: 12450, }; function handleStart() { console.log("Starting", course.title); } return ( <main> <CourseHeader title={course.title} level={course.level} instructor={course.instructor} /> <CourseStats lessons={course.lessons} duration={course.duration} students={course.students} /> <CourseAction onStart={handleStart} /> </main> ); }

Key points

  • Props are values a parent passes to a child, written as attributes.
  • The child receives them as one object - the first parameter.
  • Quotes pass strings; braces pass any JavaScript value.
  • A bare prop is true: <CourseCard published />.
  • Type props with a named type; untyped props fail in strict TypeScript.
  • Props flow one way, parent to child.
  • Props are read-only - React throws in development if you write to them.
  • Functions and JSX can be props too.

Quick check before you move on

What does CourseCard receive from <CourseCard title="React" lessons={192} />?
One object: { title: "React", lessons: 192 }, as its first parameter.
What is the difference between lessons="192" and lessons={192}?
The first passes the string "192", the second the number 192. Adding 1 gives "1921" and 193.
What does <CourseCard published /> pass?
published: true.
What happens if a component writes to props.title?
In development React throws "Cannot assign to read only property", and the component fails. In production it silently works - so never do it.

Interview questions

What are props?

The inputs to a component. A parent passes them as JSX attributes, React collects them into one object, and the component receives that object as its first argument.

Are props mutable?

No. They belong to the parent. React freezes the props object in development, so writing to it throws; in production it would silently work, which is why you never do it. Values that change belong in state.

What is one-way data flow?

Data moves down the tree through props, from parent to child. Children cannot change what they receive; they ask the parent to change it by calling a function prop.

lessons="192" versus lessons={192}?

Quotes always pass a string; braces pass the JavaScript value, here a number. With TypeScript the string is a type error; without it, arithmetic silently goes wrong.

How do you type props in TypeScript?

Declare a type for the props object and annotate the parameter: function CourseCard({ title }: CourseCardProps). TypeScript then checks every place the component is used.

When would you pass an object instead of individual props?

When the component represents that object, like a CourseCard for a course. When the component is generic, pass only the fields it needs so it does not depend on a particular data shape.

Quiz

  1. 1.

    What does <CourseCard lessons={100} /> pass? A. A string B. A number C. A boolean D. A function

  2. 2.

    What does <CourseCard lessons="100" /> pass? A. A number B. A boolean C. A string D. An object

  3. 3.

    In a Vite React TypeScript project, does this compile? function CourseCard({ title }) { return <h2>{title}</h2>; } A. Yes B. No

  4. 4.

    Where does a child normally get its props from? A. The browser B. The database C. Its parent component D. CSS

  5. 5.

    Can props hold functions? A. Yes B. No

  6. 6.

    Which describes data flowing from parent to child? A. Props B. CSS C. State D. The DOM

Comments

Sign in to leave a comment. Your name and photo come from Google; nothing else is shared.

Loading comments...