← Back to all tutorials

The Let Keyword

Understand let — block-scoped variables, the temporal dead zone, and how let fixes common var pitfalls.

The Let Keyword

let declares a block-scoped variable that can be reassigned. It fixes the scoping problems of var and is the go-to choice when you need a variable whose value changes.

Basic Syntax

let score = 0;
score = 10;        // ✅ Reassignment is allowed
score = score + 5; // ✅
console.log(score); // 15

let vs var — Block Scoping

// var leaks out of blocks
for (var i = 0; i < 3; i++) {
    // ...
}
console.log(i);  // 3 ← var leaked out of the for block!

// let stays in its block
for (let j = 0; j < 3; j++) {
    // ...
}
console.log(j);  // ❌ ReferenceError: j is not defined ← safe!

The Classic var Bug

// The infamous closure-in-loop problem:
for (var i = 0; i < 3; i++) {
    setTimeout(() => console.log(i), 100);
}
// Output: 3, 3, 3 ← All see the same i (3 after loop ends)

// Fixed with let:
for (let i = 0; i < 3; i++) {
    setTimeout(() => console.log(i), 100);
}
// Output: 0, 1, 2 ← Each iteration gets its own i

With let, each loop iteration creates a new scope with its own i. With var, there's only one i shared across all iterations.

The Temporal Dead Zone (TDZ)

// var is hoisted and initialized to undefined
console.log(a);  // undefined (no error)
var a = 10;

// let is hoisted but NOT initialized
console.log(b);  // ❌ ReferenceError: Cannot access 'b' before initialization
let b = 10;

The time between entering the scope and the let declaration is called the Temporal Dead Zone. Accessing the variable during this time throws an error — making bugs easier to catch.

No Re-declaration

// var allows re-declaration (silently overwrites!)
var name = "Alice";
var name = "Bob";   // No error — silently replaced!

// let does NOT allow re-declaration
let age = 25;
let age = 30;  // ❌ SyntaxError: Identifier 'age' has already been declared

let in Different Scopes

let x = 10;  // Global scope

if (true) {
    let x = 20;  // Block scope — different variable!
    console.log(x);  // 20
}

console.log(x);  // 10 — the outer x is unchanged

The inner let x is a completely separate variable that shadows the outer one.

var vs let vs const — Complete Comparison

Featurevarletconst
ScopeFunctionBlockBlock
HoistedYes (initialized undefined)Yes (but TDZ)Yes (but TDZ)
Re-declaration✅ Allowed❌ Error❌ Error
Reassignment✅ Allowed✅ Allowed❌ Error
Must initializeNoNoYes

When to Use let

SituationUse
Loop counter (for, while)let
Value reassigned laterlet
Accumulator or running totallet
Value never changesconst (prefer this!)

The Modern Rule

// ✅ Default to const
const apiUrl = "https://api.example.com";
const users = [];

// ✅ Use let only when you need to reassign
let isLoading = true;
// ... some async work ...
isLoading = false;

// ❌ Never use var in modern code
var old = "avoid this";

Key Takeaways

  • let is block-scoped and can be reassigned (unlike const)
  • Fixes the loop closure bug that plagued var
  • The Temporal Dead Zone prevents access before declaration
  • Cannot be re-declared in the same scope
  • Modern rule: use const by default, let when reassignment is needed, never var