How to Use Recursion in JavaScript: Full Tutorial
Recursion is a function that calls itself. Learn base cases, the call stack, practical examples like factorial and tree traversal, and when recursion beats iteration.
Recursion is a function that calls itself. Each call works on a smaller or simpler version of the problem until it reaches a stopping condition called the base case. When the base case is met, the function stops calling itself and the results unwind back up.
Here is the simplest recursive function:
function countdown(n) {
if (n <= 0) {
console.log("Done!");
return;
}
console.log(n);
countdown(n - 1);
}Calling it with a starting number prints each step on the way down to zero, one call at a time, until the base case ends the chain:
countdown(3);
// 3
// 2
// 1
// Done!countdown calls itself with n - 1 each time. When n reaches 0, the base case fires and the recursion stops.
The Two Essential Parts of Recursion
Every recursive function needs exactly two things:
- Base case: the condition that stops the recursion. Think of it as the "simplest possible input" where you know the answer without recursing further.
- Recursive case: the function calls itself with a modified argument that moves closer to the base case.
function factorial(n) {
// Base case: 0! = 1, 1! = 1
if (n <= 1) return 1;
// Recursive case: n! = n * (n-1)!
return n * factorial(n - 1);
}
console.log(factorial(5)); // 120The base case is n <= 1. The recursive case reduces n by 1 each call. Without the base case, factorial would call itself forever.
How the Call Stack Works During Recursion
Each function call adds a frame to the call stack. When a recursive function calls itself, the previous call pauses and waits for the inner call to return:
Each call stacks on top of the previous one. When the base case is reached, the stack unwinds from the top down, multiplying the results as it goes.
Practical Example: Summing an Array
function sum(arr) {
if (arr.length === 0) return 0; // Base case: empty array
return arr[0] + sum(arr.slice(1)); // Recursive case: first + rest
}
console.log(sum([1, 2, 3, 4, 5])); // 15The recursive case takes the first element and adds it to the recursive sum of the rest. Each call passes a smaller array until the array is empty.
Practical Example: Tree Traversal
Recursion is the natural way to traverse nested structures, since a tree is made of smaller trees and a function that handles one node can call itself on each child:
const fileSystem = {
name: "root",
children: [
{ name: "src", children: [{ name: "index.js" }, { name: "utils.js" }] },
{ name: "public", children: [{ name: "index.html" }, { name: "style.css" }] },
],
};The function that walks this structure prints a node, then recursively calls itself on every child in the node's children array:
function printTree(node, indent = 0) {
console.log(" ".repeat(indent) + node.name);
if (node.children) {
for (const child of node.children) {
printTree(child, indent + 2);
}
}
}Calling printTree on the root produces an indented listing where deeper files sit further to the right, since the indent grows by two spaces with every level of recursion:
printTree(fileSystem);
// root
// src
// index.js
// utils.js
// public
// index.html
// style.cssEach node prints itself, then recursively prints its children. The base case is a node with no children (a leaf). The recursive case handles nodes with a children array.
Practical Example: Flattening a Nested Array
A nested array is just another tree, so the same recursive shape applies: check each item, and if it is itself an array, recurse into it instead of just copying it over:
function flatten(arr) {
let result = [];
for (const item of arr) {
if (Array.isArray(item)) {
result = result.concat(flatten(item)); // Recursive case
} else {
result.push(item); // Base case: not an array
}
}
return result;
}Passing in an array with several levels of nesting produces one flat list regardless of how deep the original nesting went:
console.log(flatten([1, [2, [3, 4], 5], 6]));
// [1, 2, 3, 4, 5, 6]When the function encounters a nested array, it recursively flattens that array and concatenates the result. When it encounters a non-array value, it adds it directly.
Recursion vs Iteration
Any recursive function can be written as a loop, and vice versa. The choice is about clarity and safety, not capability:
| Recursion | Iteration | |
|---|---|---|
| Best for | Naturally nested or divide-and-conquer problems | Linear problems with a known count |
| Stack usage | One frame per call, risk of overflow | Constant, no extra stack frames |
| Readability | Often clearer for tree-like data | Often clearer for simple counting |
// Recursive
function factorialRec(n) {
if (n <= 1) return 1;
return n * factorialRec(n - 1);
}
// Iterative
function factorialIter(n) {
let result = 1;
for (let i = 2; i <= n; i++) {
result *= i;
}
return result;
}When to use recursion:
- The data is naturally nested (trees, file systems, JSON).
- The problem divides cleanly into smaller identical subproblems.
- The recursive solution is clearer and more readable than the loop equivalent.
When to use iteration:
- The problem is linear and the loop count is known.
- The recursion depth could be very large.
- Performance or memory is critical and tail call optimization is not available.
For how to avoid recursion pitfalls, see Preventing Stack Overflow in JavaScript Recursion. For closures, which power many recursive patterns, see JavaScript Closures Deep Dive: Complete Guide.
Rune AI
Key Insights
- Recursion is a function calling itself with modified arguments until a base case stops it.
- Every recursive function needs a base case. Without one, the call stack overflows.
- Recursion shines for tree traversal, nested data, and problems that naturally divide into subproblems.
- Each recursive call adds a frame to the call stack. Deep recursion can cause stack overflow.
- Any recursive function can be rewritten as a loop. Choose the form that is clearer for your problem.
Frequently Asked Questions
When should I use recursion over a loop?
Does recursion always use more memory than a loop?
Can arrow functions be recursive?
Conclusion
Recursion is a function that calls itself with a smaller problem until it reaches a base case. It is elegant for nested data structures, mathematical sequences, and divide-and-conquer algorithms. The key skill is identifying the base case -- the condition that stops the recursion. Without it, the function calls itself forever until the stack overflows.
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.