let vs const in JavaScript
The difference between let and const in JavaScript is reassignment. Not importance, not seriousness, not whether the value is an object, and definitely not whether the code looks professional enough. const means the binding cannot be pointed at a different value after it is initialized. let means the binding can be reassigned.
That distinction is small, but it cleans up a surprising amount of beginner confusion.
Contents
- The short rule
- const blocks reassignment
- const does not freeze objects
- Use let for changing state
- Both let and const are block scoped
- const inside loops is normal
- When to choose each one
- Key Takeaways
The short rule
Use const when the variable should keep pointing at the same value.
const courseName = "JavaScript Foundations";
const maxRetries = 3;
const users = ["Ada", "Grace"];
Use let when the variable needs to be reassigned.
let syncStatus = "waiting";
syncStatus = "synced";
That is the rule. The rest of the article is mostly about the places people accidentally add extra mythology.
You can practice the basic decision in the Choose the Keyword exercise.
const blocks reassignment
A const variable must be initialized when it is declared, and it cannot be reassigned later.
const planName = "Starter";
planName = "Pro";
// TypeError: Assignment to constant variable.
JavaScript is protecting the binding named planName. Once that name points to "Starter", the same name cannot be redirected to "Pro".
This is why const works well for fixed labels, configuration values, imported helpers, DOM references, and intermediate results that should not be replaced later.
const price = 75;
const quantity = 3;
const subtotal = price * quantity;
console.log(subtotal); // 225
subtotal is calculated once. If it should not be recalculated by assigning a new value to the same variable, const says so.
const does not freeze objects
This is the classic trap:
const profile = {
name: "Ada",
role: "admin",
};
profile.role = "editor";
console.log(profile.role); // "editor"
That code is allowed. The profile binding still points at the same object. The object changed, but the variable was not reassigned.
This is not allowed:
const profile = {
name: "Ada",
role: "admin",
};
profile = {
name: "Grace",
role: "editor",
};
// TypeError: Assignment to constant variable.
If you need to prevent object changes, const is not enough. You need a different technique, such as creating new objects instead of mutating old ones, or using Object.freeze() for shallow runtime protection.
const settings = Object.freeze({
theme: "dark",
});
settings.theme = "light";
// TypeError in strict mode. In non-strict scripts, the assignment is ignored.
console.log(settings.theme); // "dark" if the script continues
Even Object.freeze() is shallow. Nested objects can still change unless they are frozen too. Tiny language footnote, enormous source of false confidence. A classic.
Use let for changing state
Use let when reassignment is part of the meaning of the code.
let retries = 0;
retries = retries + 1;
retries = retries + 1;
console.log(retries); // 2
A retry count changes. A loading status changes. A selected tab changes. A running total changes. Those are honest let cases.
let status = "idle";
status = "loading";
status = "success";
The mistake is using let because a value feels temporary:
let fullName = `${firstName} ${lastName}`;
If fullName is never reassigned, const is clearer:
const fullName = `${firstName} ${lastName}`;
Temporary and mutable are different ideas. A value can be short-lived and still not change.
The Fix the Counter exercise focuses on this exact bug: a variable declared with const but updated like state.
Both let and const are block scoped
let and const are block scoped. A block is a pair of braces.
if (true) {
const message = "inside";
let count = 1;
}
console.log(message);
// ReferenceError: message is not defined
Variables declared inside the block are not available outside it.
That makes let and const safer than var, which uses function scope and can leak out of blocks.
if (true) {
var oldStyle = "visible outside";
}
console.log(oldStyle); // "visible outside"
Modern JavaScript code almost always prefers let and const over var. The real choice is usually between const and let, with var left for reading older code and learning why older code occasionally looks like it made a bet with chaos.
const inside loops is normal
People sometimes avoid const in loops because the value changes on each iteration. The trick is that each iteration gets a new binding.
const names = ["Ada", "Grace", "Linus"];
for (const name of names) {
console.log(name);
}
name is not reassigned inside one loop iteration. The loop creates a fresh name binding for each item.
Use let for the loop counter in a traditional for loop because the counter is reassigned:
for (let index = 0; index < names.length; index += 1) {
console.log(names[index]);
}
Use const for each item in a for...of loop when you do not reassign the item variable:
for (const name of names) {
console.log(name.toUpperCase());
}
The for...of guide covers that loop style in more detail.
When to choose each one
This table is boring in the best possible way:
| Situation | Use | Example |
|---|---|---|
| Fixed value | const | const taxRate = 0.13; |
| Calculated result | const | const total = price * quantity; |
| Object or array reference that is not replaced | const | const items = []; |
| Counter that changes | let | let attempts = 0; |
| Status that changes | let | let status = "idle"; |
| Value filled in later | let | let selectedUser = null; |
| Traditional loop counter | let | for (let i = 0; i < 3; i += 1) |
One useful habit is to start with const. If you need reassignment, change that variable to let.
const productName = "Workshop Pass";
const unitPrice = 75;
let quantity = 2;
quantity = quantity + 1;
const subtotal = unitPrice * quantity;
That habit gives each variable a sharper meaning. productName, unitPrice, and subtotal are stable. quantity is the moving piece.
Key Takeaways
constprevents reassignment of the variable binding. It does not make objects or arrays immutable.letis for variables whose value changes over time.- Start with
const, then useletwhen reassignment is part of the code's meaning. letandconstare block scoped, which makes them safer thanvarfor modern JavaScript.constis normal insidefor...ofloops because each iteration gets a fresh binding.