How to Write Nested Loops in JavaScript Tutorial
A nested loop is a loop inside another loop. Learn how nested loops work and why the inner loop runs completely for each outer iteration, with grids and multi-dimensional data.
A JavaScript nested loop is a loop placed inside the body of another loop. When the outer loop runs once, the inner loop runs all the way through. This creates a multiplication effect: if the outer loop runs N times and the inner loop runs M times, the inner body executes N x M times total.
Nested loops are the natural way to work with grids, tables, matrices, and any data organized in rows and columns.
How a Nested Loop Executes
A nested loop always runs the inner loop to completion for every single pass of the outer loop, which is the one rule that explains all of its behavior. The simplest nested loop pairs two counters:
for (let i = 1; i <= 3; i++) {
for (let j = 1; j <= 2; j++) {
console.log(`i=${i}, j=${j}`);
}
}The outer loop holds i steady while the inner loop counts fully through j, then the outer loop advances and the inner loop restarts. Output:
i=1, j=1
i=1, j=2
i=2, j=1
i=2, j=2
i=3, j=1
i=3, j=2The outer loop runs 3 times. For each outer iteration, the inner loop runs 2 times. Total: 3 x 2 = 6 lines of output.
The diagram shows the exact sequence: the inner loop completes fully before the outer loop advances to its next value. Think of it like a clock -- the second hand (inner) makes a full rotation before the minute hand (outer) ticks forward once.
Counter Naming Convention
By convention, i is the outer counter, j is the first inner counter, and k is the second inner counter:
for (let i = 0; i < 2; i++) {
for (let j = 0; j < 2; j++) {
for (let k = 0; k < 2; k++) {
console.log(i, j, k);
}
}
}This prints all 8 combinations of 0 and 1 across three positions. Using i, j, k makes the nesting level obvious at a glance.
Avoid naming nested loop counters a, b, c or x, y, z. Stick to the convention so other developers can read your code without mental translation.
Printing a Grid
The most practical use of nested loops is generating rows and columns:
const rows = 3;
const cols = 4;
for (let r = 0; r < rows; r++) {
let row = "";
for (let c = 0; c < cols; c++) {
row += `[${r},${c}] `;
}
console.log(row);
}The outer loop starts a fresh row string, the inner loop fills in every column of that row, and then the completed row gets printed. Output:
[0,0] [0,1] [0,2] [0,3]
[1,0] [1,1] [1,2] [1,3]
[2,0] [2,1] [2,2] [2,3]The outer loop builds one row at a time. The inner loop fills each cell in that row. After the inner loop finishes, the row string is complete and gets printed. Then the outer loop moves to the next row.
Iterating Over a 2D Array
A two-dimensional array is an array of arrays. Nested loops are the standard way to visit every element:
const matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
for (let i = 0; i < matrix.length; i++) {
for (let j = 0; j < matrix[i].length; j++) {
console.log(`matrix[${i}][${j}] = ${matrix[i][j]}`);
}
}The outer loop selects a row by index, and the inner loop walks across every column in that row before moving to the next one. Output:
matrix[0][0] = 1
matrix[0][1] = 2
matrix[0][2] = 3
matrix[1][0] = 4
matrix[1][1] = 5
matrix[1][2] = 6
matrix[2][0] = 7
matrix[2][1] = 8
matrix[2][2] = 9Notice matrix[i].length in the inner condition. Each row could have a different number of columns, so the inner loop uses the current row's length rather than a fixed value. This is safer than assuming every row has the same width.
Finding All Pairs
Nested loops are the go-to tool for comparing every item against every other item:
const colors = ["red", "green", "blue"];
for (let i = 0; i < colors.length; i++) {
for (let j = i + 1; j < colors.length; j++) {
console.log(`${colors[i]} + ${colors[j]}`);
}
}The inner loop always starts one position ahead of the outer loop, so every pair is compared exactly once and nothing is paired with itself. Output:
red + green
red + blue
green + blueStarting the inner loop at j = i + 1 avoids duplicate pairs like green-plus-red after red-plus-green, and avoids pairing an item with itself. This pattern shows up in comparison logic, combination generation, and any task where order does not matter.
Breaking Out of Nested Loops
A plain break only exits the innermost loop:
for (let i = 1; i <= 3; i++) {
for (let j = 1; j <= 3; j++) {
if (j === 2) break;
console.log(i, j);
}
}The break only stops the inner loop each time it fires, so the outer loop keeps advancing through all three of its own values. Output:
1 1
2 1
3 1The inner loop stops as soon as j reaches 2, but the outer loop keeps running. To stop the outer loop from inside the inner loop, use a labeled statement:
outerLoop: for (let i = 1; i <= 3; i++) {
for (let j = 1; j <= 3; j++) {
if (i === 2 && j === 1) break outerLoop;
console.log(i, j);
}
}Because the label targets the outer loop, the break stops both loops together the moment the condition matches, instead of letting the outer loop continue. Output:
1 1
1 2
1 3The label outerLoop: marks the outer for loop. When break outerLoop runs, both loops stop immediately. Use labels sparingly -- they are powerful but can make control flow hard to follow. For more on break, see the break statement guide.
Common Mistakes
Using the same counter variable for both loops. Each nesting level needs its own counter:
// WRONG: uses i for both loops
for (let i = 0; i < 3; i++) {
for (let i = 0; i < 3; i++) { // Shadows outer i
console.log(i); // Only sees inner i
}
}Accidentally writing an N-squared algorithm. A nested loop over an array of length N runs N x N iterations. For large N, this can be slow.
Before writing a nested loop, ask: could I use a Map or Set instead? Could I sort first and use two pointers? Not every pair problem needs a nested loop.
Forgetting to reset the inner state. If the inner loop builds up a value, make sure it starts fresh for each outer iteration:
// RIGHT: row resets for each outer iteration
for (let r = 0; r < 3; r++) {
let row = ""; // Fresh start each row
for (let c = 0; c < 3; c++) {
row += "*";
}
console.log(row);
}If the row variable were declared outside both loops instead of reset inside the outer one, each row would append to the previous one instead of starting fresh.
For more on how the for loop syntax works, and how to loop through arrays with a single loop, see the linked guide.
Rune AI
Key Insights
- A nested loop is a loop placed inside the body of another loop.
- The inner loop runs to completion for each single iteration of the outer loop.
- Use i for the outer counter and j for the inner counter as a convention.
- Nested loops are essential for grids, tables, matrices, and pattern generation.
- Break only exits the innermost loop; use labels to break outer loops.
Frequently Asked Questions
How many levels of nesting can I use?
Do nested loops make my code slow?
Can I use break inside a nested loop?
Conclusion
Nested loops are the standard way to work with grids, tables, and multi-dimensional data in JavaScript. The key rule is simple: the inner loop runs completely for every single iteration of the outer loop. Keep nesting shallow, name your counters clearly, and always check whether a nested loop is the right tool before reaching for one.
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.