How to flatten an array in JavaScript: flat(), flatMap() and deep nesting
Flatten nested JavaScript arrays with flat(), flat(Infinity) and flatMap(), see why flat(Infinity) and recursion fail on very deep input, and use a tested iterative version that does not.
Updated 5 min read

In this article
To flatten an array in JavaScript, choose between flat() with a depth, JavaScript flatMap() to map and flatten in one step, and hand-written recursive, iterative or lazy versions. This JavaScript flatten array guide also covers deep nesting, speed and common mistakes.
To flatten an array in JavaScript, use array.flat() for one level, array.flat(depth) for a known depth and array.flat(Infinity) for any depth. To transform and flatten in one step, use flatMap(). These built-ins cover nearly every case. The exceptions are extremely deep nesting, where flat(Infinity) runs out of stack, and old one-liners such as reduce with concat, which are much slower than they look. This guide shows each method with results, measures them, and gives a version that handles any depth.
Tested with Node.js 24.19.0 (V8 13.6). Every result shown in a comment was printed by running the code; the same code runs in current Chrome, Firefox and Safari.
flat() and flatMap() arrived in ES2019 and work in every current browser and in Node.js 11 or later, so you no longer need a polyfill or Lodash's _.flatten and _.flattenDeep for this. Many older answers use recursion with forEach and Array.isArray; the hand-written versions below show when that is still worth doing.
flat(): one level, a set depth, or every level
const nested = [1, [2, [3, [4]]]];
console.log(nested.flat()); // [ 1, 2, [ 3, [ 4 ] ] ] (one level by default)
console.log(nested.flat(2)); // [ 1, 2, 3, [ 4 ] ]
console.log(nested.flat(Infinity)); // [ 1, 2, 3, 4 ]
console.log([1, , 3, [4, , 6]].flat()); // [ 1, 3, 4, 6 ] (holes are removed)
const orders = [{ id: 1, items: ['pen', 'ink'] }, { id: 2, items: ['pad'] }];
console.log(orders.flatMap((order) => order.items)); // [ 'pen', 'ink', 'pad' ]
console.log([1, 2, 3].flatMap((n) => (n % 2 ? [n, n * 10] : []))); // [ 1, 10, 3, 30 ]flat() returns a new array and leaves the original unchanged. Note the fourth line: it removes holes in sparse arrays, so the result can be shorter than you expect. It only flattens real arrays; strings, typed arrays, Sets and array-like objects are kept as single values.
flatMap(): map and flatten one level
flatMap(fn) is the same as map(fn).flat() but makes one pass. It is the cleanest way to turn each item into zero, one or several items, as the last example above does by returning an empty array to drop even numbers. It only flattens one level; if your callback returns nested arrays, call flat() on the result.
Writing your own: recursive, iterative and lazy
You rarely need a hand-written version, but it is a common interview question and there is one real reason to have one: depth. All three functions below give the same result as flat(Infinity):
// Recursive: short and clear, but every level of nesting uses a stack frame.
export function flattenDeep(array) {
const out = [];
for (const item of array) {
if (Array.isArray(item)) out.push(...flattenDeep(item));
else out.push(item);
}
return out;
}
// Iterative: an explicit stack, so any depth works and order is kept.
export function flattenIterative(array) {
const out = [];
const stack = [[array, 0]];
while (stack.length) {
const top = stack[stack.length - 1];
const [current, index] = top;
if (index === current.length) { stack.pop(); continue; }
top[1]++;
const item = current[index];
if (Array.isArray(item)) stack.push([item, 0]);
else out.push(item);
}
return out;
}
// Lazy: yields values one at a time, useful for very large inputs.
export function* flattenLazy(array) {
for (const item of array) {
if (Array.isArray(item)) yield* flattenLazy(item);
else yield item;
}
}Deep nesting: where flat(Infinity) breaks
We built an array nested 100,000 levels deep and flattened it with each method:
let deep = [0];
for (let i = 1; i <= 100_000; i++) deep = [deep, i];
deep.flat(Infinity); // RangeError: Maximum call stack size exceeded
flattenDeep(deep); // RangeError: Maximum call stack size exceeded
flattenIterative(deep); // 100001 itemsV8 implements flat() recursively, so it fails at the same kind of depth as a recursive function. Ordinary data, such as API responses or a tree of menu items, is never nested anywhere near that deeply. But if the depth comes from user input, such as a pasted JSON document or a generated tree, use the iterative version so a hostile input cannot crash the process.
Which method is fastest?
We flattened 1,000 arrays of 100 two-item arrays (200,000 numbers in total), 20 runs each, on Node.js 24.19.0 on an Apple M2 laptop:
| Method | Time per run |
|---|---|
flattenIterative | 2.5 ms |
flat(2) | 3.1 ms |
flat(Infinity) | 3.4 ms |
flattenDeep (recursive) | 4.3 ms |
| Generator, spread into an array | 22 ms |
reduce((acc, x) => acc.concat(x), []), one level only | 67 ms |
The built-in and the plain loops are all within a few milliseconds of each other, so choose flat() for readability. Avoid the reduce plus concat pattern still found in many older answers: concat copies the whole accumulator on every step, so the work grows with the square of the input, and it flattened only one level while taking twenty times longer. If you need reduce, push into the accumulator instead of concatenating. The generator is slower but uses almost no extra memory if you consume it with for...of instead of spreading it.
Common mistakes
- Expecting
flat()to flatten everything. The default depth is 1. PassInfinitywhen nesting is unknown. - Using
toString()orjoin()and splitting.[1, [2, [3]]].toString().split(',')returns strings, not numbers, and breaks on values containing commas and on objects. - Spreading into
concatwith huge arrays.[].concat(...arrays)passes every inner array as an argument and can exceed the engine's argument limit on very large inputs;arrays.flat()has no such limit. - Confusing arrays with objects. Flattening a nested object into dotted keys, such as
{ "a.b": 1 }, is a different task that needs its own function.
Once arrays are flat, comparing them is the next common task; see how to compare arrays in JavaScript correctly.
Frequently asked questions
How do I flatten a deeply nested array in JavaScript?
Use
array.flat(Infinity). For input that might be nested tens of thousands of levels deep, such as untrusted JSON, use an iterative function with an explicit stack, becauseflat(Infinity)and recursion throw a stack overflow.What is the difference between flat() and flatMap()?
flat(depth)only flattens.flatMap(fn)callsfnon each item and then flattens the result by exactly one level, in a single pass.Does flat() change the original array?
No.
flat()andflatMap()return a new array. They do remove holes from sparse arrays.Which browsers support Array.prototype.flat()?
All current browsers and Node.js 11 or later. It has been part of the language since ECMAScript 2019.
Your next customer could start right here.
Introduce your tool, product or business to readers exploring technology.
Explore placements and request a private quote
Sources
- Array.prototype.flat() — MDN Web Docs
- Array.prototype.flatMap() — MDN Web Docs
- Array.prototype.concat() — MDN Web Docs
- yield* — MDN Web Docs
Written with substantial AI assistance. Facts checked against cited sources. Editorial policy.
Comments
No comments yet. Be the first to join the conversation.
Leave a comment
Every comment is read by a moderator before it appears. Your email address is never published; it is used only to contact you about this comment.
More from Cloudsoft Zone

Industry updateJavaScript
tsrs explained: can a Rust port make TypeScript checks 8x faster?
tsrs is an experimental Rust port of the TypeScript compiler from Cloudflare and VoidZero. What the 8x speed and 70% memory claims mean, and how to test it.
By Cloudsoft Zone

Troubleshooting guideDevOps
Laravel works locally, fails in production: trace runtime, configuration and request differences
Diagnose the Laravel works locally fails production problem by locating the first failing boundary: runtime, cached values, routing, permissions, dependencies or assets.
By Cloudsoft Zone

TutorialDevOps
Laravel log management: rotation, retention and safe production debugging
Set up Laravel log management with deliberate channels, retention, file ownership, rotation responsibility and useful incident context without recording secrets.
By Cloudsoft Zone
