JS Spread Operator for Arrays: Complete Tutorial
Learn every use of the spread operator (...) with arrays: copying, merging, inserting elements, converting iterables, and passing array elements as function arguments.
The spread operator looks like three dots: .... When used with an array, it expands the array's elements into individual values wherever a list of values is expected. It was introduced in ES6 and quickly became one of the most used features in modern JavaScript.
The spread operator does not mutate anything. It reads elements from an array and places them into a new context -- a new array literal, a function call, or another iterable position.
Copying an Array
The most basic use of spread is creating a shallow copy of an array. Wrapping an existing array in a new array literal with the spread syntax in front of it produces a brand new array containing the same elements:
const original = [1, 2, 3];
const copy = [...original];
console.log(copy); // [1, 2, 3]
console.log(copy === original); // false (different array objects)This replaces the older original.slice() pattern. It is shorter and the intent is clearer, since you can see immediately that a copy is being made rather than reading past an unrelated-looking method name.
Because it is a shallow copy, changing the copy does not affect the original, at least as long as the elements are plain values rather than objects:
const fruits = ["apple", "banana"];
const copy = [...fruits];
copy.push("orange");
console.log(fruits); // ["apple", "banana"] (unchanged)
console.log(copy); // ["apple", "banana", "orange"]But if the array contains objects, the objects themselves are shared. Modifying an object in the copy also changes the original.
Merging Arrays
Spread is the cleanest way to combine multiple arrays:
const part1 = [1, 2];
const part2 = [3, 4];
const part3 = [5, 6];
const all = [...part1, ...part2, ...part3];
console.log(all); // [1, 2, 3, 4, 5, 6]You control the order completely, since elements appear in exactly the sequence you write the spreads, regardless of which array they originally came from:
const early = ["morning"];
const late = ["evening"];
console.log([...late, ...early]); // ["evening", "morning"]
console.log([...early, ...late]); // ["morning", "evening"]For a full comparison of merging techniques including concat() and push() with spread, see How to Merge Two Arrays in JavaScript.
Inserting Elements at Any Position
Spread lets you insert elements at the beginning, middle, or end of a new array without mutating:
const middle = [2, 3];
// Insert at the beginning
console.log([1, ...middle]); // [1, 2, 3]
// Insert at the end
console.log([...middle, 4]); // [2, 3, 4]
// Insert on both sides
console.log([1, ...middle, 4]); // [1, 2, 3, 4]This is much cleaner than using unshift() and push() separately, and it does not mutate the original array.
Converting Iterables to Arrays
Spread works on any iterable, not just arrays. This means you can convert strings, Sets, Maps, and NodeLists into arrays:
// String to array of characters
console.log([..."hello"]); // ["h", "e", "l", "l", "o"]
// Set to array (removes duplicates automatically)
const unique = [...new Set([1, 2, 2, 3, 3, 4])];
console.log(unique); // [1, 2, 3, 4]
// NodeList to array (DOM elements)
// const buttons = [...document.querySelectorAll("button")];Array.from() does the same thing and is sometimes clearer for these conversions, but spread is shorter when you are already inside an array literal.
Passing Array Elements as Function Arguments
Before spread, if you had an array of values and wanted to pass them as separate arguments to a function, you used Function.prototype.apply(). Spread makes this straightforward:
const scores = [85, 92, 78, 95, 88];
const highest = Math.max(...scores);
console.log(highest); // 95
const lowest = Math.min(...scores);
console.log(lowest); // 78Math.max() expects individual numbers, not an array. The spread operator unpacks the array so each element becomes a separate argument.
This also works with your own functions:
function introduce(first, second, third) {
console.log(`${first}, meet ${second} and ${third}.`);
}
const names = ["Alice", "Bob", "Carol"];
introduce(...names);
// "Alice, meet Bob and Carol."Spread vs Other Array Operations
| Task | Old Way | Spread Way |
|---|---|---|
| Copy array | arr.slice() | [...arr] |
| Merge arrays | a.concat(b) | [...a, ...b] |
| Array to arguments | Math.max.apply(null, arr) | Math.max(...arr) |
| String to array | str.split("") | [...str] |
Spread is not always a drop-in replacement, concat() can take mixed values and arrays in one call, and slice() can extract a portion instead of copying the whole array. But for the common cases listed above, spread reads more clearly and takes less typing than the older alternatives it replaces.
Common Mistakes
Confusing spread with rest parameters. They use the same ... syntax but do opposite things, and mixing them up is a common source of confusion for anyone new to modern JavaScript syntax:
// REST: collects arguments into an array (in function definition)
function logAll(...items) {
console.log(items); // ["a", "b", "c"]
}
logAll("a", "b", "c");
// SPREAD: expands an array into arguments (in function call)
const arr = ["x", "y", "z"];
logAll(...arr); // ["x", "y", "z"]Rest packs values together. Spread unpacks them again.
Expecting spread to deep-copy. If your array contains objects, the copy shares references to the same inner objects:
const original = [{ name: "Alice" }, { name: "Bob" }];
const copy = [...original];
copy[0].name = "Changed";
console.log(original[0].name); // "Changed" (surprising!)For immutable updates to nested data, see Copying Nested Objects with the JS Spread Operator.
Now that you understand spread, the next step is learning the array iteration methods that replace traditional loops. Start with map vs forEach to see how spread patterns combine with functional array methods.
Rune AI
Key Insights
- The spread operator ... expands an array into individual elements inside a new context.
- Use [...arr] to create a shallow copy of an array.
- Use [...a, ...b] to merge arrays without mutating the originals.
- Use [...arr, newItem] to insert elements at any position.
- Spread works on any iterable: arrays, strings, Sets, Maps, and NodeLists.
Frequently Asked Questions
Does the spread operator create a deep copy?
What is the difference between spread and rest parameters?
Can I spread a string?
Conclusion
The spread operator is one of the most useful additions to modern JavaScript. It replaces multiple older patterns -- concat() for merging, slice() for copying, and apply() for function arguments -- with a single clean syntax. Once you start using spread, you will reach for it constantly when working with arrays.
More in this topic
JavaScript While Loop Explained: A Complete Guide
A while loop repeats code as long as a condition stays true. Learn the syntax, see practical examples, and understand when a while loop is the right choice over a for loop.
JavaScript Loops Tutorial: for, while, do while
Loops let JavaScript repeat code without writing it twice. Learn the three core loop types, what each one does, and when to choose one over another.
How to Loop Through Arrays Using JS for Loops Guide
Looping through arrays is the most practical use of JavaScript for loops. Learn how to access each element by index, avoid off-by-one errors, and choose between for, for...of, and forEach.