- Home
- /
- Tutorials
- /
- React Tutorial
- /
- Spreading and Forwarding Props
Components and Props
Spreading and Forwarding Props
Spreading props lets a wrapper accept everything the underlying element accepts, without listing every attribute by hand.
Forwarding the rest
Rest destructuring separates the props you handle from the ones you pass straight through. The component takes what it needs and forwards the remainder untouched.
A button wrapper that stays flexible
jsx
function Button({ tone = "neutral", ...rest }) {
const background = tone === "primary" ? "#007a96" : "#e2e8f0"
const color = tone === "primary" ? "#ffffff" : "#0f172a"
// Everything not named above — onClick, disabled, type, aria-label — is
// handed to the real button.
return (
<button
{...rest}
style={{ background, color, border: 0, borderRadius: 6, padding: "6px 12px" }}
/>
)
}
const root = ReactDOM.createRoot(document.getElementById("root"))
root.render(
<div style={{ display: "flex", gap: 8 }}>
<Button tone="primary" onClick={() => alert("clicked")}>
Click me
</Button>
<Button disabled title="Explained by the title attribute">
Disabled
</Button>
</div>
)Without the spread, supporting title would mean adding a title prop, then aria-label, then type - for every attribute a button has.
Order decides who wins
A spread is an object literal, so a later key overwrites an earlier one. Where you put {...rest} decides whether callers can override your defaults or not.
Before or after
jsx
function AlwaysBlue(props) {
// Own value last: the caller cannot change the colour.
return <p {...props} style={{ color: "blue" }} />
}
function DefaultBlue(props) {
// Own value first: the caller can override it.
return <p style={{ color: "blue" }} {...props} />
}
const root = ReactDOM.createRoot(document.getElementById("root"))
root.render(
<div>
<AlwaysBlue style={{ color: "red" }}>Still blue — the spread came first.</AlwaysBlue>
<DefaultBlue style={{ color: "red" }}>Red — the caller's value came last.</DefaultBlue>
</div>
)Neither is correct in general. Decide whether the value is a default the caller may replace, or a rule the component enforces, and place the spread accordingly.
When not to spread
- Into your own components -
<Profile {...props} />hides what is actually being passed, and the next reader cannot tell what the component needs. - From untrusted data - spreading an object you did not construct can set attributes you never intended.
- When the list is short - three named props are clearer than a spread.
The pattern earns its place at the boundary with real DOM elements, where the attribute list is long and standard. Inside your own tree, naming props explicitly is almost always the better trade.
Unknown props reach the DOM
Spreading onto a real element passes every key straight to the DOM, and React warns in the console about attributes it does not recognise - Received `true` for a non-boolean attribute is the usual one. It is harmless, but it is a sign that a prop meant for your component leaked through.
The fix is to pull that prop out of the rest before spreading. Naming it in the destructure both consumes it and documents that the component uses it, which is why { tone, ...rest } above never forwards tone to the button.
