On this page

Destructuring, Spread & Rest

3 min read

Open any React file and you will see const { name } = props, [...items] and ({ a, ...rest }). This is the syntax that makes modern React code short. It looks similar for three different jobs, so it helps to separate them.

Definition

FeatureWhat it doesWhere it appears
DestructuringPulls values out of an object or array into variablesLeft side of =, function parameters
Spread (...)Expands an array or object into separate itemsRight side, inside [], {}, or a call
Rest (...)Collects the leftover items into one array or objectLeft side, in a pattern or parameter list

The same three dots mean "expand" or "collect" depending on the position. If it is creating or passing values, it is spread. If it is receiving values, it is rest.

Destructuring

js
1const user = { name: "Asha", age: 25, city: "Pune" };
2
3const { name, age } = user; // by property name
4const { city: town } = user; // rename
5const { country = "India" } = user; // default if undefined
6const { address: { zip } } = { address: { zip: 411001 } }; // nested
7
8const [first, second] = ["a", "b", "c"]; // arrays use position
9const [, , third] = ["a", "b", "c"]; // skip with commas

Swapping two variables without a temp:

js
1[a, b] = [b, a];

Defaults only apply when the value is undefined, not for null:

js
1const { x = 5 } = { x: null }; // x is null, not 5

Rest

js
1const { name, ...others } = user; // others = { age: 25, city: "Pune" }
2const [head, ...tail] = [1, 2, 3, 4]; // tail = [2, 3, 4]
3
4function sum(...nums) { // nums is a real array
5 return nums.reduce((a, b) => a + b, 0);
6}
7sum(1, 2, 3); // 6

The rest element must be last. [...a, b] as a pattern is a syntax error.

Spread

js
1const a = [1, 2];
2const b = [...a, 3, 4]; // [1, 2, 3, 4]
3
4const base = { theme: "dark", size: "md" };
5const next = { ...base, size: "lg" }; // later keys win: { theme: "dark", size: "lg" }
6
7Math.max(...[3, 9, 2]); // 9, spread an array into arguments

Order matters. In { ...base, size: "lg" } the new size overrides. In { size: "lg", ...base } the old one wins.

Spread makes a shallow copy. Nested objects are still shared, as we saw in the reference topic.

Here Is What Breaks

js
1const { a } = null; // TypeError: cannot destructure null
2const { a } = undefined; // same

Protect against missing data with a default: const { a } = obj ?? {};.

Also, spreading a string or iterable into an object gives surprising results, and spread does not copy getters or non-enumerable properties the way you might expect.

In React

Props destructuring in the parameter list:

jsx
1function Card({ title, subtitle = "", children }) {
2 return <div><h2>{title}</h2><p>{subtitle}</p>{children}</div>;
3}

Forwarding the rest of the props to a DOM element:

jsx
1function Button({ variant, ...rest }) {
2 return <button className={variant} {...rest} />; // onClick, disabled, etc. pass through
3}

State updates with spread:

jsx
1setUser((prev) => ({ ...prev, name: "Ravi" }));
2setItems((prev) => [...prev, newItem]);

Hooks return arrays, so you rename them freely with array destructuring: const [count, setCount] = useState(0). A custom hook returning an object lets callers pick only what they need.

Summary

1Destructuring → pull values out (objects by name, arrays by position)
2Rest → collect the leftovers, must be last
3Spread → expand items into a new array, object or call
4Same ... symbol → expand when creating, collect when receiving
5Defaults → apply to undefined only
6Spread copy → shallow
7React → props destructuring, forwarding ...rest, immutable updates

So far we used objects freely. Next we look at how objects inherit from each other, using prototypes.

Next TopicPrototypes & Inheritance