
Learn how JavaScript's flat() and flatMap() work: default depth, flat(Infinity), dropping holes, and using flatMap as a combined map and filter.
Martin Ferret
August 11, 2026
For a long time, flattening a nested array in JavaScript meant reaching for a loop or a reduce. The flat() and flatMap() methods, introduced in ES2019, cover that need in a way that is readable and direct.
flat() creates a new array with all sub-array elements pulled up by one or more levels. By default, it only pulls up a single level.
const nested = [1, [2, 3], [4, 5]];
const flat = nested.flat();
console.log(flat); // → [1, 2, 3, 4, 5]
Read it as: "lift the elements of the sub-arrays up by one level."
flat() With No Argument Is Not EnoughBy default, flat() only descends one level. As soon as the structure is nested deeper than that, the result surprises you.
const deep = [1, [2, [3, [4]]]];
deep.flat(); // → [1, 2, [3, [4]]] only one level removed
deep.flat(2); // → [1, 2, 3, [4]]
deep.flat(Infinity); // → [1, 2, 3, 4] flattens every level, whatever the depth
The mental model: flat() asks "how many levels should I go down?", and the default answer is: just one.
| Call | Behavior |
|---|---|
flat() | depth of 1 (the default) |
flat(2) | descends 2 levels |
flat(Infinity) | flattens the entire depth, however deep |
flat(0) | changes nothing, returns a shallow copy |
flat() also drops the empty slots (holes) of a sparse array as it goes, something map() does not do.
const sparse = [1, , 3, [4, , 6]];
console.log(sparse.flat()); // → [1, 3, 4, 6] the holes are gone
Sometimes that is exactly what you want, and sometimes it is a surprise: worth keeping in mind if the array can contain holes.
flatMap(): Map, Then FlattenflatMap() combines map() and flat() in a single pass: it applies a function to each element, then flattens the result by one level.
const numbers = [1, 2, 3];
const result = numbers.flatMap(n => [n, n * 2]);
console.log(result); // → [1, 2, 2, 4, 3, 6]
With map() alone, you would get an array of arrays that you then have to flatten yourself:
numbers.map(n => [n, n * 2]);
// → [[1, 2], [2, 4], [3, 6]]
In other words, arr.flatMap(fn) is identical to arr.map(fn).flat() (a flat() of depth 1), but slightly more efficient than calling the two methods separately, since it does not build a full intermediate array of arrays.
flatMap() as map and filter CombinedBecause the callback can return an array of any length, returning [] drops the element and returning several values adds them. That turns flatMap() into a combined map and filter.
const values = [5, -2, 8, -1, 3];
// Keep the positives, double them, and drop the negatives
const positives = values.flatMap(n => n > 0 ? [n * 2] : []);
console.log(positives); // → [10, 16, 6]
This is a common idiom: where a filter followed by a map needed two passes, flatMap does the filtering and the transforming in a single expression. Like map(), it also skips holes in a sparse array.
💡
flatMap()only flattens one level, and that level is not configurable: there is noflatMap(fn, depth). If the callback returns arrays nested more than one level deep, chain aflat()afterward:arr.flatMap(fn).flat(Infinity). This is deliberate:flatMapis built for the common one-to-many case, not for deep flattening.
[1, 2].flatMap(n => [[n]]);
// → [[1], [2]] only one level removed, the inner arrays remain
flat() and flatMap() replace verbose loops and reduce calls with two readable methods. Use flat() to level out a structure you have already built, with Infinity when the depth is unknown. Use flatMap() to transform and flatten in a single pass, where it shines whenever each element produces zero, one, or several values.
In any code that builds lists out of other lists, these two methods make intent explicit, and intent is what separates readable code from clever code.
Get the latest news and updates on developer certifications. Content is updated regularly, so please make sure to bookmark this page or sign up to get the latest content directly in your inbox.

Tighter TypeScript in <script setup>
How Vue sharpened TypeScript support for defineProps, defineEmits, defineModel, and defineSlots, and how to write clearer component contracts.
Reza Baar
Aug 26, 2026

Intl.NumberFormat and Intl.RelativeTimeFormat: The Traps
Intl.NumberFormat and Intl.RelativeTimeFormat replace your formatting library. Defaults, traps, and the 58x performance mistake to avoid.
Martin Ferret
Aug 25, 2026

Component Architecture for React Server Components
React Server Components let each component fetch its own data, so pages compose instead of prop-drilling. How that reshapes architecture, loading boundaries, and UX.
Aurora Scharff
Aug 20, 2026