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: