Labels
A label is an identifier with a colon attached to a statement — usually a loop. Once a statement is labelled, you can break or continue to that exact label from anywhere inside it, including from nested loops. Labels are the only built-in way to break out of an outer loop in JavaScript, but most code shouldn't need them.
Syntax
outer: for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
if (grid[r][c] === target) {
break outer; // exits the loop named "outer"
}
}
}The label is just a name — pick something descriptive. Common choices: outer, rows, scan, search. Labels live in their own namespace, so you can reuse names that exist as variables without conflict, though it's not a habit to cultivate.
Labelled break
break label jumps to the statement after the labelled statement. The most common use is escaping a nested search.
function findPair(matrix, target) {
let found = null;
search: for (let r = 0; r < matrix.length; r++) {
for (let c = 0; c < matrix[r].length; c++) {
if (matrix[r][c] === target) {
found = [r, c];
break search;
}
}
}
return found;
}
console.log(findPair([[1, 2], [3, 4]], 3));[ 1, 0 ]
Labelled continue
continue label jumps to the next iteration of the labelled loop, skipping the rest of every enclosed loop. It's rarer but occasionally exactly what you want.
const grid = [
[1, 2, 3],
[4, -1, 6],
[7, 8, 9],
];
rows: for (let r = 0; r < grid.length; r++) {
for (let c = 0; c < grid[r].length; c++) {
if (grid[r][c] < 0) continue rows; // abandon this whole row
process(grid[r][c]);
}
}Without the label, continue would only skip to the next column. The label lets us say "drop the row and move on".
Labels can attach to any statement
Technically you can label any statement, not just loops — including a plain block. Once labelled, break label exits the block.
done: {
if (!user) break done;
if (!user.active) break done;
charge(user);
notify(user);
}This is sometimes called a "labelled block" pattern — a structured-goto style. It works, but it's almost always clearer as guard clauses inside a function with return.
When (rarely) labels are useful
Deeply nested numeric loops where extracting a function would obscure the algorithm — game grids, image processing, dynamic programming inner loops.
Code that genuinely needs to continue an outer loop based on a condition discovered in an inner one.
Performance-critical hot loops where extracting a function would add call overhead — but measure before assuming.
Why most code shouldn't use them
Labels are unusual in modern JavaScript. Most readers have rarely or never seen one. The same job can almost always be done with one of these:
Extract a function and use
return— the cleanest fix, and the one that signals intent.Set a flag and check it in the outer loop's condition. Slightly verbose but obvious.
Throw a sentinel error — heavy-handed, but works for very deep nesting if a function refactor is impossible.
Use array methods like
find,some,findIndex— they break early by design.
The labelled version
outer: for (const row of matrix) {
for (const value of row) {
if (value === target) {
console.log("found");
break outer;
}
}
}The refactored version most teams prefer
function contains(matrix, target) {
for (const row of matrix) {
if (row.includes(target)) return true;
}
return false;
}
if (contains(matrix, target)) console.log("found");What labels are not
Labels are not goto. You cannot break to a label that comes after the current position; the label has to enclose the break. You also can't jump into an arbitrary point in code. JavaScript deliberately left that out — and the language is healthier for it.