Prefer const and let over var
Use block-scoped const and let declarations instead of function-scoped var to avoid hoisting bugs and unintended variable mutations.
- Use const for values that are never reassigned
- Use let when reassignment is needed
- Never use var — it has function scope and is hoisted, causing subtle bugs
- const does not mean immutable — objects and arrays declared with const can still be mutated
Rule Details
Block-scoped declarations introduced in ES2015 (const and let) solve real bugs caused by var's function scope and hoisting behavior.
Code Examples
// ❌ Bad: var leaks out of the block
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100)
}
// Logs: 3, 3, 3 — not 0, 1, 2!
// ✅ Good: let is block-scoped
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100)
}
// Logs: 0, 1, 2// ❌ Bad: var is hoisted and accessible before declaration
console.log(name) // undefined, not ReferenceError
var name = 'Alice'
// ✅ Good: let/const throw ReferenceError in the temporal dead zone
console.log(name) // ReferenceError: Cannot access 'name' before initialization
let name = 'Alice'Why It Matters
var's function scope and hoisting behavior regularly causes bugs where variables are accessible outside the block they were declared in. const and let enforce block scope and make code intent clearer — const signals that a binding should not be reassigned, which helps readers understand data flow.
const vs let
// ✅ Use const when a binding won't be reassigned
const MAX_RETRIES = 3
const apiUrl = 'https://api.example.com'
const user = fetchUser() // the binding is constant, not the object
// ✅ Use let when reassignment is needed
let count = 0
count++
let status = 'pending'
status = 'complete'const Does Not Mean Immutable
// ✅ const prevents reassignment of the binding
const user = { name: 'Alice', role: 'admin' }
user.role = 'viewer' // ✅ mutation is allowed
user = { name: 'Bob' } // ❌ TypeError: assignment to constant variable
// To make an object truly immutable, use Object.freeze
const config = Object.freeze({ debug: false, version: '1.0' })
config.debug = true // silently fails in sloppy mode, throws in strict modeEnforce with ESLint
Add these rules to your ESLint config:
{
"rules": {
"no-var": "error",
"prefer-const": "error"
}
}Verification
Automated Checks
- Verify the behavior in the browser after the code change, not only in static analysis.
- Inspect DevTools Network or Performance panels when the rule affects loading or execution order.
- Test the primary user flow and one edge case triggered by the changed script path.
Manual Checks
- Confirm the code still behaves correctly when the feature is delayed, lazy-loaded, or fails.
Use with AI
Copy these prompts to use with your AI assistant, or install the MCP server to use directly from Claude, Cursor, or Windsurf.
Check
Verify implementation
Scan this JavaScript file for any use of var and flag every occurrence with line numbers.
Fix
Auto-fix issues
Replace all var declarations with const or let as appropriate. Use const when the variable is never reassigned, let otherwise.
Explain
Learn more
Explain why const and let are preferred over var, covering scope, hoisting, and temporal dead zone.
Review
Code review
Review scripts, client components, and browser execution paths related to Prefer const and let over var. Flag exact imports, event handlers, runtime side effects, or blocking operations that violate the rule, and state how the change should be verified in the browser.