Skip to content

Understanding Props

Props are how data flows between React components. They're like function parameters, but for components. Mastering props is essential for building React applications.

What You'll Learn

  • What props are and why they matter
  • How to pass data from parent to child components
  • TypeScript interfaces for props
  • The special children prop
  • Props vs state (brief intro)
  • Real examples from our codebase

What Are Props?

Props (short for "properties") are how you pass data into components.

Simple Analogy

Think of a component as a function:

// Regular function with parameters
function greet(name) {
  return `Hello, ${name}!`
}

greet("Alice")  // "Hello, Alice!"
greet("Bob")    // "Hello, Bob!"

Components work the same way:

// Component with props
function Greeting({ name }) {
  return <h1>Hello, {name}!</h1>
}

<Greeting name="Alice" />  // Displays: Hello, Alice!
<Greeting name="Bob" />    // Displays: Hello, Bob!

Same component, different data!

Basic Props Example

Simple Button Component

interface ButtonProps {
  text: string
  color: string
}

function Button({ text, color }: ButtonProps) {
  return (
    <button className={`bg-${color}-600 px-4 py-2 text-white rounded`}>
      {text}
    </button>
  )
}

// Usage:
<Button text="Click me!" color="blue" />
<Button text="Submit" color="green" />
<Button text="Cancel" color="red" />

Result: Same component, three different buttons!

Anatomy of Props

1. Interface Definition (TypeScript)

interface ButtonProps {
  text: string       // Required string
  color: string      // Required string
  size?: string      // Optional (? mark)
}

Why TypeScript interfaces?

  • Catches errors before runtime
  • Auto-completion in VS Code
  • Self-documenting code

2. Destructuring Props

function Button({ text, color, size }: ButtonProps) {
  // Now you can use: text, color, size
}

Destructuring pulls properties out of the props object.

Alternative (not recommended):

function Button(props: ButtonProps) {
  return <button>{props.text}</button>  // More verbose
}

3. Using Props in JSX

function Button({ text, color }: ButtonProps) {
  return (
    <button className={`bg-${color}-600`}>
      {text}
    </button>
  )
}

Use props inside { } to embed values in JSX.

Real Example: LandingLayout

Let's examine our actual codebase. Open:

components/layout/LandingLayout.tsx

The Complete Component

import Header from "./Header";
import Footer from "./Footer";

interface LandingLayoutProps {
  children: React.ReactNode;
}

export default function LandingLayout({ children }: LandingLayoutProps) {
  return (
    <div className="min-h-screen flex flex-col">
      <Header />
      <main className="flex-grow">
        {children}
      </main>
      <Footer />
    </div>
  );
}

Breaking It Down

Interface (Lines 4-6):

interface LandingLayoutProps {
  children: React.ReactNode;
}
  • children: Special prop (more on this below)
  • React.ReactNode: Type for any renderable content

Function with Props (Line 8):

export default function LandingLayout({ children }: LandingLayoutProps) {
  • Destructures children from props
  • Types it with the interface

Using children (Line 12):

<main className="flex-grow">
  {children}
</main>
  • Renders whatever was passed between the component tags

How It's Used

In app/page.tsx:

<LandingLayout>
  <section>Homepage content here</section>
  <section>More content</section>
  <section>Even more content</section>
</LandingLayout>

Everything between <LandingLayout> and </LandingLayout> becomes children!

Result:

<div class="min-h-screen flex flex-col">
  <Header />
  <main class="flex-grow">
    <section>Homepage content here</section>
    <section>More content</section>
    <section>Even more content</section>
  </main>
  <Footer />
</div>

Pattern: Layout components use children to wrap page content

The Children Prop

children is a special prop for nested content.

Without Children

<Header />  // Self-closing, no children

With Children

<Card>
  <h2>Title</h2>
  <p>Content</p>
</Card>

Everything inside becomes children!

Card Example

interface CardProps {
  children: React.ReactNode
  title?: string
}

function Card({ children, title }: CardProps) {
  return (
    <div className="border rounded-lg p-4">
      {title && <h3>{title}</h3>}
      {children}
    </div>
  )
}

// Usage:
<Card title="Welcome">
  <p>This is the card content</p>
  <button>Click me</button>
</Card>

Renders as:

<div class="border rounded-lg p-4">
  <h3>Welcome</h3>
  <p>This is the card content</p>
  <button>Click me</button>
</div>

Props Flow: Parent → Child

Data flows one way: from parent to child.

Example

// Parent component
function HomePage() {
  return (
    <ArticleLayout title="About Us" author="K12worX Team">
      <p>Article content here</p>
    </ArticleLayout>
  )
}

// Child component
interface ArticleLayoutProps {
  title: string
  author: string
  children: React.ReactNode
}

function ArticleLayout({ title, author, children }: ArticleLayoutProps) {
  return (
    <article>
      <h1>{title}</h1>
      <p>By {author}</p>
      <div>{children}</div>
    </article>
  )
}

Data flow:

  1. HomePage passes title, author, children to ArticleLayout
  2. ArticleLayout receives and uses those props
  3. ArticleLayout cannot modify HomePage's data
  4. Flow is one-way (downward)

Diagram:

HomePage
  │
  ├── title="About Us"
  ├── author="K12worX Team"
  └── children=<p>Article content</p>
      │
      ▼
ArticleLayout (receives props)

Optional vs Required Props

Required Props

interface UserCardProps {
  name: string      // Required
  email: string     // Required
}

Must provide when using component:

<UserCard name="Alice" email="alice@example.com" />

Missing prop = TypeScript error!

Optional Props

interface UserCardProps {
  name: string
  email: string
  avatar?: string    // Optional (? mark)
}

Can omit:

<UserCard name="Alice" email="alice@example.com" />

Or provide:

<UserCard name="Alice" email="alice@example.com" avatar="/alice.jpg" />

Default Values

interface ButtonProps {
  text: string
  color?: string
}

function Button({ text, color = "blue" }: ButtonProps) {
  return (
    <button className={`bg-${color}-600`}>
      {text}
    </button>
  )
}

// Uses default blue:
<Button text="Click" />

// Overrides with red:
<Button text="Click" color="red" />

Pattern: Use = "value" for default values

Different Types of Props

String Props

<Component title="Hello" />

Number Props

<Component count={5} />        // Curly braces for non-strings!

Boolean Props

<Component isActive={true} />
<Component isActive />         // Shorthand for true

Array Props

<Component items={["A", "B", "C"]} />

Object Props

<Component user={{ name: "Alice", age: 25 }} />

Function Props

<Button onClick={() => alert("Clicked!")} />

Pattern: Use { } for any non-string value!

Props in Navigation (Review)

Remember our Navigation component? It uses props implicitly!

const navItems = [
  { name: "About", href: "/about" },
  // ...
]

{navItems.map((item) => (
  <Link
    key={item.name}
    href={item.href}      // Passing props to Link!
  >
    {item.name}
  </Link>
))}

Link component receives:

  • key prop (for React)
  • href prop (where to go)
  • children prop (the text "About")

Each Link gets different props from the array!

Common Props Patterns

Pattern 1: Layout Component

interface LayoutProps {
  children: React.ReactNode
}

function Layout({ children }: LayoutProps) {
  return (
    <div>
      <Header />
      <main>{children}</main>
      <Footer />
    </div>
  )
}

Use for: Wrapping pages with consistent structure

Pattern 2: Display Component

interface UserCardProps {
  name: string
  email: string
  avatar?: string
}

function UserCard({ name, email, avatar }: UserCardProps) {
  return (
    <div className="card">
      {avatar && <img src={avatar} alt={name} />}
      <h3>{name}</h3>
      <p>{email}</p>
    </div>
  )
}

Use for: Displaying data with custom formatting

Pattern 3: Interactive Component

interface ButtonProps {
  text: string
  onClick: () => void
}

function Button({ text, onClick }: ButtonProps) {
  return (
    <button onClick={onClick}>
      {text}
    </button>
  )
}

Use for: Components that respond to user actions

Pattern 4: Configurable Component

interface AlertProps {
  type: "info" | "warning" | "error"
  message: string
}

function Alert({ type, message }: AlertProps) {
  const colors = {
    info: "bg-blue-100 text-blue-900",
    warning: "bg-yellow-100 text-yellow-900",
    error: "bg-red-100 text-red-900",
  }

  return (
    <div className={`p-4 rounded ${colors[type]}`}>
      {message}
    </div>
  )
}

Use for: Components with different variants

Props vs State

You'll hear about both props and state. What's the difference?

Props

  • Data passed from parent
  • Cannot be changed by the component receiving them
  • Like function parameters
function Greeting({ name }: { name: string }) {
  // name is a prop - cannot change it
  return <h1>Hello, {name}</h1>
}

State

  • Data owned by the component
  • Can be changed by the component
  • Causes re-render when changed
function Counter() {
  const [count, setCount] = useState(0)

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
    </div>
  )
}

Rule of Thumb

  • Props: Data comes from parent, doesn't change
  • State: Data lives in component, changes over time

We'll cover state in detail in later sections!

Hands-On Exercises

Exercise 1: Create a Greeting Component

Create a simple component with props.

Steps:

  1. Create a new file: components/content/Greeting.tsx
  2. Write this code:
interface GreetingProps {
  name: string
}

export default function Greeting({ name }: GreetingProps) {
  return <h2>Welcome, {name}!</h2>
}
  1. Use it in app/page.tsx:
import Greeting from '@/components/content/Greeting'

// Somewhere in your JSX:
<Greeting name="Your Name" />
  1. Save and view in browser!

Exercise 2: Add More Props

Extend the Greeting component.

Add these props:

interface GreetingProps {
  name: string
  role?: string       // Optional
  showWelcome?: boolean  // Optional
}

export default function Greeting({ name, role, showWelcome = true }: GreetingProps) {
  return (
    <div>
      {showWelcome && <h2>Welcome, {name}!</h2>}
      {role && <p>Role: {role}</p>}
    </div>
  )
}

Try different usage:

<Greeting name="Alice" />
<Greeting name="Bob" role="Student" />
<Greeting name="Carol" role="Teacher" showWelcome={false} />

Exercise 3: Card Component with Children

Create a reusable card component.

// components/content/Card.tsx
interface CardProps {
  title: string
  children: React.ReactNode
  color?: string
}

export default function Card({ title, children, color = "blue" }: CardProps) {
  return (
    <div className={`border-2 border-${color}-500 rounded-lg p-6`}>
      <h3 className="text-xl font-bold mb-4">{title}</h3>
      {children}
    </div>
  )
}

Use it:

<Card title="My Card" color="purple">
  <p>This is inside the card</p>
  <button>Click me</button>
</Card>

Exercise 4: Button with Click Handler

Create a button that accepts an onClick function.

// components/content/FancyButton.tsx
interface FancyButtonProps {
  text: string
  onClick: () => void
  color?: string
}

export default function FancyButton({ text, onClick, color = "blue" }: FancyButtonProps) {
  return (
    <button
      onClick={onClick}
      className={`px-6 py-3 bg-${color}-600 text-white rounded-lg hover:bg-${color}-700 transition`}
    >
      {text}
    </button>
  )
}

Use it:

<FancyButton
  text="Say Hello"
  onClick={() => alert("Hello!")}
  color="green"
/>

Common Questions

Q: Can I change props inside a component?

A: No! Props are read-only. You can use them, but not modify them.

// ❌ Don't do this
function Component({ name }: { name: string }) {
  name = "Changed"  // Error!
  return <div>{name}</div>
}

// ✅ Do this (if you need to modify)
function Component({ initialName }: { initialName: string }) {
  const [name, setName] = useState(initialName)
  return <div>{name}</div>
}

Q: What if I forget to pass a required prop?

A: TypeScript will show an error in VS Code before you even run the code!

Q: Can I pass a component as a prop?

A: Yes! Components can be passed as props.

interface CardProps {
  icon: React.ReactNode
  title: string
}

function Card({ icon, title }: CardProps) {
  return (
    <div>
      {icon}
      <h3>{title}</h3>
    </div>
  )
}

<Card icon={<StarIcon />} title="Featured" />

Q: How many props can a component have?

A: As many as you need! But if you have 10+ props, consider breaking the component into smaller pieces.

Q: What's the difference between props and attributes?

A: HTML elements have attributes (class, id). React components have props. Similar concept, different terminology.

Troubleshooting

Problem: "Property 'title' does not exist on type..."

Cause: Missing prop in component call

Solution: Make sure you pass all required props

// ❌ Missing title
<Card name="Test" />

// ✅ Correct
<Card title="Test" name="Example" />

Problem: Props are undefined inside component

Check:

  1. Did you destructure props correctly?
  2. Did you pass the prop when using component?
  3. Check spelling (case-sensitive!)

Problem: TypeScript error about prop types

Solution: Make sure prop types match the interface

// ❌ Wrong type
<Component count="5" />  // count should be number

// ✅ Correct
<Component count={5} />  // Use {} for numbers

Problem: Children not showing

Check:

  1. Did you accept children in props interface?
  2. Did you render {children} in JSX?
  3. Did you pass content between component tags?

Quick Reference

Define props interface:

interface Props {
  name: string        // Required
  age?: number        // Optional
}

Use props in component:

function Component({ name, age }: Props) {
  return <div>{name} is {age} years old</div>
}

Pass props:

<Component name="Alice" age={25} />

Children prop:

interface Props {
  children: React.ReactNode
}

<Component>
  <p>This becomes children</p>
</Component>

Default values:

function Component({ color = "blue" }: Props) {
  // color defaults to "blue" if not provided
}

String vs other types:

<Component
  title="Hello"           // String (no braces)
  count={5}              // Number (braces)
  isActive={true}        // Boolean (braces)
  onClick={() => {}}     // Function (braces)
/>

What You've Accomplished

You now understand:

  • ✅ What props are and how they work
  • ✅ How to define props with TypeScript interfaces
  • ✅ How to pass data from parent to child
  • ✅ The special children prop
  • ✅ Optional vs required props
  • ✅ Different types of props
  • ✅ Props vs state (basics)

This is a huge milestone! Props are fundamental to React.

Next Steps

You've completed Chapter 04! You now know how to read React code. Here's what comes next:

Continue to Section 05: Styling Basics →

Or review any topic: