JavaScript example

How to flatten an array in JavaScript

6 min read Updated Sep 2026 Runs in an isolated runtime
Quick answer

Call flat(): nested.flat() turns [[1, 2], [3, 4]] into [1, 2, 3, 4]. It removes one level of nesting by default; pass a depth, or nested.flat(Infinity) to flatten every level. It returns a new array and leaves the original alone.

"Flattening" merges an array of arrays into a single array. Since ES2019 JavaScript has two built-ins for it: flat(), which flattens to a depth you choose, and flatMap(), which maps each element and flattens the result one level. The older answers you still find everywhere, [].concat.apply([], arr) and reduce with concat, work but have real limits, and a hand-written recursive flatten is only worth it when you need rules flat() can't express. Each example runs on this page: hit Run, then edit the code and run it again.

1flat() and flat(Infinity)Recommended

arr.flat(depth) returns a new array with every sub-array spread into it, recursing up to depth levels. The depth defaults to 1, which is exactly right for the common case of a plain array of arrays. For input nested to an unknown depth, pass Infinity. The deeper examples print through JSON.stringify because Node collapses arrays nested more than two levels down to [Array].

flat.js

Output

Prints [ 1, 2, 3, 4, 5, 6 ], then [1,2,[3,[4]]] for the default depth, [1,2,3,[4]] for depth 2, [1,2,3,4] for Infinity, and finally the unchanged [1,[2,[3,[4]]]]. The most common surprise is expecting flat() with no argument to flatten everything: it removes one level and stops. flat is supported in every current browser and in Node 11+, so there is no reason to reach for a library or a polyfill.

2flatMap() (map, then flatten one level)

When each element turns into several values, map gives you an array of arrays that you then have to flatten. flatMap(fn) does both in one pass: it is arr.map(fn).flat() without the intermediate array. Because the callback can return an empty array, it also doubles as a filter and a map at once.

flat-map.js

Output

map prints [ [ 'hello', 'world' ], [ 'flat', 'map' ] ]; flatMap prints [ 'hello', 'world', 'flat', 'map' ]. The filter-and-map call keeps the even numbers and emits each one twice: [ 2, 20, 4, 40 ]. The last line prints [[1],[2]]: a callback that returns nested arrays still leaves one level behind, because flatMap always flattens exactly one level. Chain .flat() after it if you need more.

3Holes, strings and objects

flat has two behaviours worth knowing before you rely on it. It removes holes, the empty slots in a sparse array, from every array it flattens. And it only spreads real arrays (anything Array.isArray accepts): strings, plain objects, Sets and array-like objects stay as single elements.

holes.js

Output

Node shows the sparse input as [ 1, <1 empty item>, 3, [ 4, <1 empty item>, 6 ] ]; flat() returns [ 1, 3, 4, 6 ], and even flat(0), which flattens nothing, returns [ 1, 3 ]. That makes arr.flat(0) a quick way to compact a sparse array. Explicit undefined and null survive: [ 1, undefined, null, 2 ]. The mixed array prints [ 'ab', 'cd', { a: 1 }, Set(1) { 7 }, { '0': 'x', length: 1 } ]: the string 'ab' is not split into characters, and the Set and the array-like object are left whole. To flatten those too, convert them first with Array.from or write your own rule, as in section 5.

4Older ways: concat.apply, spread and reduce

Before ES2019 the accepted answer was [].concat.apply([], nested). concat spreads any array argument one level into the result, and apply passes each sub-array as a separate argument. The spread version [].concat(...nested) is the same trick in newer syntax, and reduce with concat is the functional-style variant. All three flatten exactly one level.

older.js

Output

The first three lines all print [ 1, 2, 3, 4, 5, 6 ], and the mixed input gives [ 1, 2, 3, 4 ]. The catch is the last part: apply and spread turn every sub-array into a function argument, and with 500,000 of them the call fails with RangeError: Maximum call stack size exceeded, while many.flat() handles the same input and prints 500000. The reduce version has no argument limit but copies the whole accumulator on every step, which makes it O(n²) on long inputs. Keep these for code that must run on engines older than ES2019; otherwise use flat().

5A recursive flatten

flat(Infinity) already flattens any depth, so a hand-written version is for when you need different rules: flatten Sets as well, stop at a certain type, or transform leaves on the way. The pattern is short. Walk each item, recurse into arrays, and push everything else onto one shared output array, which avoids building and copying a new array at every level.

recursive.js

Output

Both flatten(deep) and deep.flat(Infinity) print [ 1, 2, 3, 4, 5, 6 ]. They differ on holes: the hand-written version prints [ 1, undefined, 2 ] where flat() prints [ 1, 2 ], because for...of reads an empty slot as undefined. flattenAll descends into the Set as well and prints [ 1, 2, 3, 4, 5 ]. Use Array.isArray rather than instanceof Array for the check, since it also recognises arrays created in another realm.

6Which should you use?

MethodDepthLarge inputBest for
arr.flat()1 (or any)FineAn array of arrays
arr.flat(Infinity)All levelsFineUnknown nesting depth
arr.flatMap(fn)1FineMap to several values, or filter + map
[].concat(...arr)1RangeError on huge arraysPre-ES2019 engines
arr.reduce((a, x) => a.concat(x), [])1Slow (O(n²))Legacy code only
recursive flatten()All levelsFineCustom rules (Sets, stop types)

Frequently asked questions

How do I flatten an array of arrays in JavaScript?

Call flat(): [[1, 2], [3, 4]].flat() returns [1, 2, 3, 4]. It flattens one level by default and returns a new array without changing the original. Before ES2019 the usual answer was [].concat.apply([], arr), which does the same for one level.

How do I flatten a deeply nested array?

Pass a depth to flat, or Infinity to remove every level: [1, [2, [3, [4]]]].flat(Infinity) returns [1, 2, 3, 4]. With no argument flat() only removes one level, so the same call returns [1, 2, [3, [4]]].

What is the difference between flat and flatMap?

flatMap(fn) is map(fn) followed by flat() with a depth of 1, done in one pass. Use it when each element produces zero or more values, for example splitting sentences into words. It has no depth argument, so chain .flat() if the callback returns arrays nested more than one level.

Why does flat() not split strings or flatten objects?

flat only spreads values for which Array.isArray is true. Strings, plain objects, Sets, Maps and array-like objects such as { 0: "x", length: 1 } are kept as single elements. Convert them first, for example with Array.from(set), or write a recursive flatten with your own check.

Does flat() remove empty slots from an array?

Yes. flat skips holes in every array it flattens, so [1, , 3].flat() returns [1, 3], and even flat(0) removes holes without flattening anything. Explicit undefined and null values are kept, because they are real elements rather than holes.

Why does [].concat(...arr) throw a RangeError?

Spread and apply pass every sub-array as a separate function argument, and engines limit how many arguments a call can take. With hundreds of thousands of sub-arrays the call fails with RangeError: Maximum call stack size exceeded. arr.flat() has no such limit.

Run it yourself

Open any of these in the full JavaScript editor — tweak, run, and share.

JavaScript playground