What the Modulo Operator Actually Does

At first glance, the modulo operator (%) can seem arbitrary. The numbers it produces often appear to follow no obvious pattern:

const what = 10 % 4; // 2
const the = 10 % 10; // 0
const heck = 4 % 10; // 4

The trick is to rethink what division really means. Instead of thinking about fractional results, consider division as the task of splitting a number into equally-sized groups of whole items.

12 ÷ 4

Take 12 ÷ 4. The result is 3 because we can place exactly 3 items in each of the 4 groups. The number 12 is cooperative here, as it divides cleanly into many configurations. But what about a number like 11?

11 ÷ 4

That equation gives 2.75, which assumes we can cut items into fractions. In the real world of programming, we often cannot. If our items are indivisible, 4 groups of 2 items use up only 8 of the 11, and we are left with 3 leftover items.

That leftover amount is the remainder — and it is exactly what the modulo operator returns. For operands that divide evenly, the remainder is zero. For those that do not, the modulo operator reports the shortfall:

12 % 4; // 0
11 % 4; // 3

A Practical Pattern: Circular Arrays

For a web developer, a common challenge involves cycling through a list of items indefinitely. Suppose you have an array of three colors — red, yellow, and blue — and you want to rotate through the list every second, wrapping back to the start after reaching the end.

A naive approach fails because a growing counter (like timeElapsed) will quickly exceed the array's valid index range. The goal is a mapping like this:

const COLORS = ['red', 'yellow', 'blue'];

getColor({ timeElapsed: 0 }); // 'red'
getColor({ timeElapsed: 1 }); // 'yellow'
getColor({ timeElapsed: 2 }); // 'blue'
getColor({ timeElapsed: 3 }); // 'red'
getColor({ timeElapsed: 4 }); // 'yellow'
getColor({ timeElapsed: 5 }); // 'blue'
getColor({ timeElapsed: 6 }); // 'red'
getColor({ timeElapsed: 7 }); // 'yellow'
getColor({ timeElapsed: 8 }); // 'blue'
// ...And so on, forever

The modulo operator provides a clean solution:

const COLORS = ['red', 'yellow', 'blue'];

function getColor({ timeElapsed }) {
  const colorIndex = timeElapsed % COLORS.length;

  return COLORS[colorIndex];
}

Here, COLORS.length is 3. As timeElapsed increments, the index is calculated as the remainder of dividing timeElapsed by 3:

const colorIndex = 0 % 3; // 0
const colorIndex = 1 % 3; // 1
const colorIndex = 2 % 3; // 2
const colorIndex = 3 % 3; // 0
const colorIndex = 4 % 3; // 1
const colorIndex = 5 % 3; // 2
const colorIndex = 6 % 3; // 0
const colorIndex = 7 % 3; // 1
const colorIndex = 8 % 3; // 2

Because the remainder of a division by 3 can only ever be 0, 1, or 2, the resulting colorIndex is always a valid array index. There is no need for conditional checks or manual resets — the operator inherently wraps the value into the range.

This circular-array pattern has broad application. Any time you need to loop through a finite set of values in a repeating sequence, the modulo operator can model that behavior with a single expression.