Default Prop Values
Not every prop needs to be required. When a prop has a sensible fallback value, you can make it optional and provide a default so that callers only pass it when they want to override the default behaviour. This is called graceful degradation — the component works correctly even when called with the bare minimum of information.
ES6 Default Parameters in Destructuring
The modern and recommended way to set default prop values is with ES6 default parameter syntax directly in the destructuring assignment. Anything you can write in a regular function's parameter list works here:
// Default values declared right in the function signature:
function Avatar({ name = 'Guest', size = 40, shape = 'circle' }) {
return (
<img
src={`https://api.dicebear.com/7.x/initials/svg?seed=${name}`}
alt={name}
width={size}
height={size}
style={{ borderRadius: shape === 'circle' ? '50%' : 4 }}
/>
)
}
// All three props are optional — all defaults kick in:
<Avatar />
// Override just the ones you care about:
<Avatar name="Alice" size={64} />Before and After: Required vs Optional Props
See how adding defaults transforms a rigid component into a flexible one:
// BEFORE — every prop is required; forgetting one breaks the UI
function Button({ label, variant, size, disabled, onClick }) {
return (
<button
className={`btn btn--${variant} btn--${size}`}
disabled={disabled}
onClick={onClick}
>
{label}
</button>
)
}
// The caller must always provide every prop:
<Button label="Save" variant="primary" size="md" disabled={false} onClick={save} />// AFTER — sensible defaults; only override what you need
function Button({
label,
variant = 'primary',
size = 'md',
disabled = false,
onClick,
}) {
return (
<button
className={`btn btn--${variant} btn--${size}`}
disabled={disabled}
onClick={onClick}
>
{label}
</button>
)
}
// Minimal call — defaults handle the rest:
<Button label="Save" onClick={save} />
// Still easy to override any default:
<Button label="Delete" variant="danger" size="sm" onClick={handleDelete} />TypeScript: Marking Props Optional
In TypeScript, pair the ? modifier in the interface with a default value in the destructuring. Props without a ? are still required, and TypeScript will error if they are omitted:
interface ButtonProps {
label: string // required — no default, no ?
variant?: 'primary' | 'secondary' | 'danger'
size?: 'sm' | 'md' | 'lg'
disabled?: boolean
onClick?: () => void
}
function Button({
label,
variant = 'primary',
size = 'md',
disabled = false,
onClick,
}: ButtonProps) {
return (
<button
className={`btn btn--${variant} btn--${size}`}
disabled={disabled}
onClick={onClick}
>
{label}
</button>
)
}Common Pattern: Boolean Props Default to false
Boolean feature-flag props almost always default to false so the component is in its simplest state by default and you "opt in" to the extra behaviour:
function Input({
type = 'text',
placeholder = '',
disabled = false, // off by default
readOnly = false, // off by default
showLabel = true, // on by default — note: not all booleans default false
label,
}) {
return (
<div>
{showLabel && <label>{label}</label>}
<input
type={type}
placeholder={placeholder}
disabled={disabled}
readOnly={readOnly}
/>
</div>
)
}
// Simple usage — only pass what differs from the default:
<Input label="Email" type="email" placeholder="you@example.com" />
// Opt-in to disabled state:
<Input label="Username" disabled />Default Values for Objects and Arrays
You can default an object or array prop, but be careful: if you define the default inside the function signature it creates a new reference every render, which can cause child re-renders. For objects and arrays define the default outside the component, or use useMemo:
// ✗ Creates a new array reference on every render
function TagList({ tags = [] }) { /* ... */ }
// ✓ Stable reference — define the default outside the component
const EMPTY_TAGS: string[] = []
function TagList({ tags = EMPTY_TAGS }) {
return (
<ul>
{tags.map((tag) => <li key={tag}>{tag}</li>)}
</ul>
)
}Legacy: defaultProps (Avoid in New Code)
Before ES6 default parameters, React offered a static defaultProps object on the component. This approach is now deprecated and will be removed in a future React version. Avoid it in new code:
// ✗ Legacy — deprecated in React 19, will be removed
function Button({ label, variant }) {
return <button className={`btn--${variant}`}>{label}</button>
}
Button.defaultProps = {
variant: 'primary',
}
// ✓ Modern — use ES6 default parameters instead
function Button({ label, variant = 'primary' }) {
return <button className={`btn--${variant}`}>{label}</button>
}Use ES6 defaults in the destructuring — they are standard JavaScript, IDE-friendly, and work great with TypeScript
Boolean props almost always default to
falseso the component is opt-inOptional string/number props benefit from a descriptive default (e.g.
name = "Guest",size = "md")Avoid
defaultPropson function components — it is deprecated and will be removedObject/array defaults should be defined outside the component or inside
useMemoto avoid creating new references on every render