Array.prototype.concat()

Non-mutating, unlike push — which is the main reason to still reach for it. Note the asymmetry: an array argument is spread, but only one level deep.

Array methodES3 (1999)Live demo
Common call
a.concat(b)
Returns
a new array; neither input is modified
Replaces
[...a, ...b] in modern code
Watch out
array arguments are SPREAD, so concat([1,2]) adds two elements, not one
array.concat(...values...values — Any number of values. Arrays are spread one level; non-arrays are appended whole. With no arguments you get a shallow copy.type: any · default: none)
→ Array

Demo

Live evaluation
Try:
Inputs
itemsnumber[]first array, comma separated
morenumber[]second array, comma separated
Output
[1, 2].concat([3, 4])
[1, 2, 3, 4]

The elements of the second array are appended to the first, producing a new array — neither input is touched. An empty argument simply contributes nothing. The behaviour the demo cannot show is what happens with a NON-array argument: concat(3) appends the number 3 as one element, while concat([3]) spreads the array and also appends 3. The two look different but give the same result, which is a frequent source of confusion.

Parameters

NameTypeRequiredDescription
...valuesanyno (none)Any number of values. Arrays are spread one level; non-arrays are appended whole. With no arguments you get a shallow copy.

Return value

Array — A NEW array holding this array's elements followed by each argument. Array arguments are flattened one level; everything else is appended as a single element.

Common patterns

Merge two arrays
The classic use; spread syntax is the modern equivalent.
const all = a.concat(b);
const all2 = [...a, ...b];
Append without mutating
Unlike push, the original is preserved — useful for state.
const next = items.concat(newItem);
Flatten one level
The pre-ES2019 idiom, before flat existed.
const flat = [].concat(...arrays);

Examples

1. Two arrays
[1, 2].concat([3, 4])
Returns
[1, 2, 3, 4]
2. A plain value
[1, 2].concat(3)
Returns
[1, 2, 3]
3. Several arguments
[1, 2].concat([3], [4])
Returns
[1, 2, 3, 4]
4. Only one level
[1, 2].concat([3, [4]])
Returns
[1, 2, 3, [4]]
5. No arguments copies
[1].concat()
Returns
[1]
6. Original untouched
const a = [1]; a.concat([2]); a
Returns
[1]

Pitfalls

1. Array arguments are spread, not appended
concat([1, 2]) adds TWO elements, not one array. When you genuinely want to append an array as a single element, push or a nested literal is what you need.
Spread, not nested
[1].concat([2, 3])
[1, 2, 3]
Wrap to nest
[1].concat([[2, 3]])
[1, [2, 3]]
2. It only flattens one level
Nested arrays inside an argument survive. This is exactly why [].concat(...arrays) was the old flatten idiom — and why it only ever flattened a single level.
Still nested
[1].concat([2, [3]])
[1, 2, [3]]
Use flat
[1, [2, [3]]].flat(Infinity)
[1, 2, 3]
3. The copy is shallow
Like slice and spread, the new array holds the SAME objects. Mutating a nested object through the result changes it in both arrays.
Shared objects
const a = [{n: 1}];
const b = a.concat();
b[0].n = 99;
a[0].n
99
Deep copy
const b = structuredClone(a);
fully independent

When to use

Use it
  • Merging arrays without mutating either
  • Appending to state you must not modify in place
  • Flattening one level on runtimes older than ES2019
Reach for something else
  • Modern code merging two arrays → [...a, ...b] reads better
  • Appending in place → push, which is faster and clearer about intent
  • Deep flattening → flat(Infinity)

Notes

Complexity
O(n + m) — every element is copied into the new array
Return
A new array; no input is modified
CPython impl
V8: Builtins-array-concat.tq, with a fast path for packed arrays
Memory
Allocates a new array holding references to the same elements
Thread-safe
Single-threaded; the sources are only read

FAQ

They do the same thing for merging arrays. Spread reads better in modern code and handles any iterable, not just arrays. concat is still useful when you want a shallow copy with no arguments, and it is marginally faster on very large arrays in some engines.

const all = [...a, ...b];

History

ES3
concat standardised in 1999.
ES2015
Spread syntax gave [...a, ...b] as a more readable merge.
ES2019
flat replaced the [].concat(...arrays) flattening idiom.