You've been writing props.name, props.age, props.email everywhere in your components. It works, but it's repetitive and can make your code harder to read. There's a better way! Props destructuring is a JavaScript feature that lets you extract prop values into variables, making your code cleaner and more professional. In this lesson, you'll learn all the destructuring patterns and when to use them.
The Problem: Repetitive Code
Without destructuring, you have to write props. before every prop you use:
function UserCard(props) {
return (
<div className="user-card">
<h2>{props.name}</h2>
<p>Age: {props.age}</p>
<p>Email: {props.email}</p>
<p>Location: {props.location}</p>
<p>Role: {props.role}</p>
<button onClick={props.onEdit}>Edit</button>
{props.isPremium && <span>⭐ Premium</span>}
</div>
)
}
// props.props.props... gets tedious!This is repetitive and makes the code harder to scan. You have to mentally filter out all those props. prefixes to understand what the component does.
The Solution: Destructuring
Destructuring extracts values from objects into individual variables. Here's the same component with destructuring:
function UserCard({ name, age, email, location, role, onEdit, isPremium }) {
return (
<div className="user-card">
<h2>{name}</h2>
<p>Age: {age}</p>
<p>Email: {email}</p>
<p>Location: {location}</p>
<p>Role: {role}</p>
<button onClick={onEdit}>Edit</button>
{isPremium && <span>⭐ Premium</span>}
</div>
)
}
// Much cleaner! No more props.props.props...Notice how we destructure in the function parameter itself. Instead of function UserCard(props), we write function UserCard({ name, age, email, ... }).
What just happened?
Instead of receiving a props object and accessing props.name, we immediately extract the values we need into separate variables: name, age, etc.
It's the same data, just cleaner syntax!
Basic Destructuring Syntax
Let's understand how destructuring works step by step:
JavaScript Object Destructuring
First, here's how destructuring works in regular JavaScript (not React):
// Regular object
const user = {
name: 'Alice',
age: 25,
email: 'alice@example.com'
}
// Without destructuring
const name = user.name
const age = user.age
const email = user.email
// With destructuring - same result, cleaner syntax
const { name, age, email } = user
console.log(name) // 'Alice'
console.log(age) // 25
console.log(email) // 'alice@example.com'In Function Parameters
// Regular function
function greet(user) {
return `Hello, ${user.name}! You are ${user.age} years old.`
}
// Function with destructuring in parameters
function greet({ name, age }) {
return `Hello, ${name}! You are ${age} years old.`
}
// Both work the same way
greet({ name: 'Alice', age: 25 }) // "Hello, Alice! You are 25 years old."In React Components
// Without destructuring
function Greeting(props) {
return <h1>Hello, {props.name}!</h1>
}
// With destructuring in parameters
function Greeting({ name }) {
return <h1>Hello, {name}!</h1>
}
// Both work exactly the same
<Greeting name="Alice" />Destructuring Patterns
1. In Function Parameters (Most Common)
// Destructure directly in the function parameter
function Button({ label, onClick, variant }) {
return (
<button
onClick={onClick}
className={`btn btn-${variant}`}
>
{label}
</button>
)
}
<Button label="Save" onClick={handleSave} variant="primary" />This is the most common and recommended approach in React. It's clean and immediately shows what props the component expects.
2. In Function Body
// Destructure inside the function body
function Button(props) {
const { label, onClick, variant } = props
return (
<button
onClick={onClick}
className={`btn btn-${variant}`}
>
{label}
</button>
)
}
// Less common but useful when you need the props object too3. Partial Destructuring
// Destructure some props, keep others in props object
function Card({ title, children, ...otherProps }) {
// title and children are destructured
// Everything else is in otherProps
return (
<div className="card" {...otherProps}>
<h2>{title}</h2>
<div>{children}</div>
</div>
)
}
<Card
title="Hello"
id="card-1"
className="special"
data-test="my-card"
>
Content
</Card>
// title → extracted
// children → extracted
// id, className, data-test → passed to div via {...otherProps}Default Values in Destructuring
You can set default values directly in the destructuring syntax:
function Button({
label = 'Click Me',
variant = 'primary',
size = 'medium',
disabled = false
}) {
return (
<button
className={`btn btn-${variant} btn-${size}`}
disabled={disabled}
>
{label}
</button>
)
}
// All props are optional with defaults
<Button />
// Uses all defaults: label="Click Me", variant="primary", size="medium"
<Button label="Save" variant="success" />
// label="Save", variant="success", size="medium" (default), disabled=false (default)💡 Default Values Syntax
{ name = 'default' }
If name is not provided or is undefined, it will be 'default'
This works for null, 0, "", and false too - only undefined triggers the default!
Renaming Props
Sometimes you want to use a different variable name than the prop name:
function UserProfile({
name: userName, // Rename 'name' to 'userName'
age: userAge, // Rename 'age' to 'userAge'
email: userEmail
}) {
return (
<div>
<h2>{userName}</h2>
<p>Age: {userAge}</p>
<p>Email: {userEmail}</p>
</div>
)
}
<UserProfile name="Alice" age={25} email="alice@example.com" />
// Useful when:
// - Avoiding naming conflicts
// - Making variable names more descriptive
// - Working with poorly named APIsNested Destructuring
You can destructure nested objects too:
function UserCard({
user: { name, age }, // Destructure nested 'user' object
address: { city, country }, // Destructure nested 'address' object
isPremium
}) {
return (
<div>
<h2>{name}, {age}</h2>
<p>{city}, {country}</p>
{isPremium && <span>⭐ Premium</span>}
</div>
)
}
// Usage:
<UserCard
user={{ name: 'Alice', age: 25 }}
address={{ city: 'Boston', country: 'USA' }}
isPremium={true}
/>Be Careful with Nested Destructuring!
While powerful, deeply nested destructuring can be hard to read. If you have more than 2 levels, consider destructuring in the function body instead:const { name, age } = userconst { city, country } = address
The Rest Operator (...)
The rest operator collects remaining props into a new object:
function Input({ label, type = 'text', ...inputProps }) {
return (
<div>
<label>{label}</label>
{/* Pass all remaining props to input */}
<input type={type} {...inputProps} />
</div>
)
}
<Input
label="Email"
type="email"
placeholder="Enter your email"
required
maxLength={100}
autoComplete="email"
/>
// label → extracted
// type → extracted
// Everything else (placeholder, required, maxLength, autoComplete) → in inputPropsThis is incredibly useful for wrapper components that need to pass props down to child elements!
Common Rest Operator Pattern
function Button({ children, variant = 'primary', ...rest }) {
return (
<button
className={`btn btn-${variant}`}
{...rest} // Passes onClick, disabled, id, etc.
>
{children}
</button>
)
}
// All these extra props get passed through:
<Button
variant="primary"
onClick={handleClick}
disabled={isLoading}
id="submit-btn"
data-testid="submit"
>
Submit
</Button>When to Destructure
✅ Always Destructure When:
- You have 1-7 props
- Props have clear, descriptive names
- You want clean, readable code
- You're writing a new component
// ✅ Perfect use case
function ProductCard({ name, price, image, onAddToCart }) {
return (
<div className="product">
<img src={image} alt={name} />
<h3>{name}</h3>
<p>${price}</p>
<button onClick={onAddToCart}>Add to Cart</button>
</div>
)
}⚠️ Consider Keeping Props Object When:
- You have many props (8+)
- You need to pass the whole props object somewhere
- Props are dynamically accessed
// ⚠️ Too many props - gets unwieldy
function ComplexForm({
firstName,
lastName,
email,
phone,
address,
city,
state,
zip,
country,
company,
jobTitle,
onSubmit
}) {
// ...
}
// ✅ Better - keep as props object
function ComplexForm(props) {
// Destructure what you need, when you need it
const { onSubmit } = props
return (
<form onSubmit={onSubmit}>
<input value={props.firstName} />
<input value={props.lastName} />
{/* etc */}
</form>
)
}🎯 Use Partial Destructuring When:
- You need a few specific props plus the rest
- Building wrapper components
- Forwarding props to child components
// 🎯 Perfect for wrappers
function StyledInput({ label, error, ...inputProps }) {
return (
<div>
<label>{label}</label>
<input {...inputProps} />
{error && <span className="error">{error}</span>}
</div>
)
}Practical Example: Before and After
Let's see a real component refactored with destructuring:
Before: Without Destructuring
function BlogPost(props) {
return (
<article className="blog-post">
<h1>{props.title}</h1>
<div className="meta">
<span>By {props.author}</span>
<span>{props.date}</span>
<span>{props.readTime} min read</span>
</div>
{props.featured && <span className="badge">Featured</span>}
<img src={props.coverImage} alt={props.title} />
<div className="content">
{props.content}
</div>
<div className="tags">
{props.tags.map((tag, index) => (
<span key={index} className="tag">{tag}</span>
))}
</div>
{props.onEdit && (
<button onClick={props.onEdit}>Edit</button>
)}
</article>
)
}After: With Destructuring
function BlogPost({
title,
author,
date,
readTime,
featured,
coverImage,
content,
tags,
onEdit
}) {
return (
<article className="blog-post">
<h1>{title}</h1>
<div className="meta">
<span>By {author}</span>
<span>{date}</span>
<span>{readTime} min read</span>
</div>
{featured && <span className="badge">Featured</span>}
<img src={coverImage} alt={title} />
<div className="content">
{content}
</div>
<div className="tags">
{tags.map((tag, index) => (
<span key={index} className="tag">{tag}</span>
))}
</div>
{onEdit && (
<button onClick={onEdit}>Edit</button>
)}
</article>
)
}
// Much cleaner and easier to read!💡 Tip: Edit the code above and click "Run" to see your changes
Common Destructuring Mistakes
1. Misspelling Prop Names
// ❌ Wrong - typo in destructuring
function User({ nmae, age }) { // 'nmae' instead of 'name'
return <div>{nmae}</div> // undefined!
}
<User name="Alice" age={25} />
// ✅ Correct - exact spelling
function User({ name, age }) {
return <div>{name}</div>
}2. Destructuring Non-Existent Props
function User({ name, age, email }) {
return (
<div>
<h2>{name}</h2>
<p>{email}</p> {/* undefined if not passed! */}
</div>
)
}
// Only passing name and age, not email
<User name="Alice" age={25} />
// ✅ Better - use default or check existence
function User({ name, age, email = 'Not provided' }) {
return (
<div>
<h2>{name}</h2>
<p>{email}</p>
</div>
)
}3. Forgetting Curly Braces
// ❌ Wrong - missing curly braces
function Button(label, onClick) { // This won't work!
return <button onClick={onClick}>{label}</button>
}
// ✅ Correct - wrap in curly braces
function Button({ label, onClick }) {
return <button onClick={onClick}>{label}</button>
}4. Over-Destructuring
// ❌ Too complex - hard to read
function Component({
user: {
profile: {
name: { first, last },
contact: { email, phone: { mobile, home } }
}
}
}) {
// What's even happening here?
}
// ✅ Better - destructure in steps
function Component({ user }) {
const { profile } = user
const { name, contact } = profile
const { first, last } = name
const { email, phone } = contact
return <div>{first} {last} - {email}</div>
}Destructuring Best Practices
1. Be Consistent
// ✅ Good - consistent destructuring style in all components
function Button({ label, onClick }) { /* ... */ }
function Input({ value, onChange }) { /* ... */ }
function Card({ title, children }) { /* ... */ }
// ❌ Inconsistent - mixing styles
function Button(props) { /* ... */ }
function Input({ value, onChange }) { /* ... */ }
function Card(props) {
const { title } = props
/* ... */
}2. Destructure Early
// ✅ Good - destructure in parameters
function UserProfile({ name, age, email }) {
return <div>{name} - {email}</div>
}
// ⚠️ Okay but unnecessary
function UserProfile(props) {
const { name, age, email } = props // Extra line
return <div>{name} - {email}</div>
}3. Use Defaults for Optional Props
// ✅ Good - clear defaults
function Button({
variant = 'primary',
size = 'medium',
disabled = false,
children
}) {
return <button>{children}</button>
}
// ❌ Less clear - checking in code
function Button({ variant, size, disabled, children }) {
const finalVariant = variant || 'primary'
const finalSize = size || 'medium'
// ...
}4. Group Related Props
// ✅ Good - related props grouped
function Form({
// User data
name,
email,
phone,
// Handlers
onSubmit,
onChange,
onCancel,
// State
isLoading,
error
}) {
// ...
}Key Takeaways
- Destructuring extracts prop values into individual variables
- Use
{ name, age }in function parameters for cleaner code - Set defaults with
{ name = 'default' } - Rename props with
{ name: userName } - Use rest operator
{ title, ...rest }to collect remaining props - Destructure in parameters for 1-7 props, in body for more
- Partial destructuring is perfect for wrapper components
- Be consistent with your destructuring style
- Avoid over-destructuring complex nested objects
- Destructuring makes code more readable and professional
- It's just JavaScript ES6 syntax, not React-specific
What's Next?
You now know how to write clean, professional React components using props destructuring! Your code is more readable and easier to maintain.
But we've been building components in isolation. How do you combine multiple components to build complex UIs? That's where component composition comes in! In the next lesson, you'll learn the art of composing small, focused components into larger, more powerful applications. Get ready to build real-world UI architectures! 🏗️