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
childrenprop - 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
childrenfrom 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:
- HomePage passes
title,author,childrento ArticleLayout - ArticleLayout receives and uses those props
- ArticleLayout cannot modify HomePage's data
- 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:
keyprop (for React)hrefprop (where to go)childrenprop (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:
- Create a new file:
components/content/Greeting.tsx - Write this code:
interface GreetingProps {
name: string
}
export default function Greeting({ name }: GreetingProps) {
return <h2>Welcome, {name}!</h2>
}
- Use it in
app/page.tsx:
import Greeting from '@/components/content/Greeting'
// Somewhere in your JSX:
<Greeting name="Your Name" />
- 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:
- Did you destructure props correctly?
- Did you pass the prop when using component?
- 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:
- Did you accept
childrenin props interface? - Did you render
{children}in JSX? - 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
childrenprop - ✅ 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: