Skip to main content
Beta: Front-End Checklist is currently in beta. Some issues are still being fixed. Thanks for your patience.

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.

Utilities
Quick take
Typical fix time 10 min
  • 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
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.

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 mode

Enforce 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.

Sources

References used to support the guidance in this rule.

Further Reading

Tools and supplementary material for exploring the topic in more depth.

Rules that often go hand-in-hand with this one.

Prefer immutable data patterns

Use spread operators, Object.assign, and array methods that return new values instead of mutating objects and arrays in place, to make data flow predictable and debugging easier.

JavaScript
Lint JavaScript code

JavaScript code is linted with ESLint to detect errors and enforce coding standards.

JavaScript
Validate external data at runtime with a schema library

Use Zod or Valibot to validate data from API responses, form inputs, localStorage, and environment variables — TypeScript types are erased at runtime and cannot protect against unexpected shapes.

JavaScript
Never use eval() or unsafe dynamic code execution

Avoid eval(), new Function(), setTimeout/setInterval with string arguments, and innerHTML with untrusted content — they execute arbitrary code and create critical XSS vulnerabilities.

JavaScript

Was this rule helpful?

Your feedback helps improve rule quality. This stays internal for now.

Loading feedback...
0 / 385