Skip to main content

What is the Strict Mode in JavaScript and How to use the "use strict" Directive

JavaScript was designed in 10 days, and its early versions were extremely forgiving. Mistakes that should have thrown errors were silently ignored. Typos in variable names accidentally created global variables. Assignments to read-only properties did nothing without warning. As the language matured, these silent failures became a real problem.

Strict mode was introduced in ES5 (2009) to fix this. It is an opt-in mechanism that changes JavaScript's behavior: silent errors become thrown exceptions, certain unsafe features are disabled, and the engine can apply additional optimizations. This guide explains exactly what strict mode does, how to enable it, every change it makes with concrete examples, and whether you still need to worry about it in modern JavaScript.

What Is Strict Mode and Why It Exists

Strict mode is a restricted variant of JavaScript that eliminates some of the language's most problematic silent behaviors. When you enable strict mode, JavaScript stops being "nice" about mistakes and starts telling you when something is wrong.

The Problem Strict Mode Solves

In normal (sometimes called "sloppy") JavaScript, the language goes out of its way to avoid errors, even when your code is clearly wrong:

// Without strict mode: this silently creates a global variable
function calculateTotal(price, tax) {
totl = price + tax; // Typo! 'totl' instead of 'total'
return totl; // No error (JS creates a global variable 'totl')
}

calculateTotal(100, 20);
console.log(totl); // 120 (leaked into global scope!)

You misspelled total as totl, but JavaScript did not complain. Instead, it silently created a new global variable called totl. In a large codebase, bugs like this can take hours to find.

With strict mode, the same mistake immediately throws an error:

"use strict";

function calculateTotal(price, tax) {
totl = price + tax; // ReferenceError: totl is not defined
return totl;
}

The bug is caught instantly, right where it happens.

Three Goals of Strict Mode

  1. Turn silent errors into explicit exceptions so bugs are found immediately
  2. Prevent unsafe patterns that make code harder to optimize or secure
  3. Reserve syntax for future JavaScript features so they can be added without breaking existing code

How to Enable Strict Mode

Strict mode is enabled by placing the exact string "use strict"; (or 'use strict';) at the very beginning of a script or function. It is not a keyword or a statement. It is a special directive that the JavaScript engine recognizes.

Script-Level Strict Mode

Place "use strict"; as the very first statement of a JavaScript file to apply strict mode to the entire script:

"use strict";

// Everything in this file runs in strict mode
let name = "Alice";
console.log(name);

The directive must be the first line of executable code. Only comments can precede it:

// This comment is fine
"use strict";

// Strict mode is active

If anything other than a comment appears before "use strict";, the directive is treated as a regular string expression and strict mode is not enabled:

let x = 10;
"use strict"; // Too late! This is just a string expression, not a directive

mistke = 5; // No error (strict mode was NOT activated)
warning

There is no visual feedback when strict mode fails to activate. The "use strict" line simply becomes a meaningless string expression. JavaScript does not warn you that the directive was ignored.

Function-Level Strict Mode

You can enable strict mode for a single function without affecting the rest of the file:

// This code runs in normal (sloppy) mode
let x = 10;

function strictFunction() {
"use strict";
// Only this function runs in strict mode

y = 20; // ReferenceError: y is not defined
}

function normalFunction() {
// This function runs in normal mode
z = 30; // No error (creates global variable (bad, but allowed))
}

Function-level strict mode is useful when you need to add strict mode to an existing codebase gradually without converting everything at once.

Strict Mode in HTML Script Tags

When using multiple <script> tags in HTML, each one has its own strict mode scope:

<script>
"use strict";
// This script runs in strict mode
let a = 1;
</script>

<script>
// This script runs in normal mode (separate scope)
b = 2; // No error (creates a global variable)
</script>

Each <script> tag is treated as an independent unit. Enabling strict mode in one does not affect others.

What Changes in Strict Mode (Complete List with Examples)

Strict mode introduces a significant number of changes. Here is every important change, organized by category, with clear examples showing the difference.

1. Accidental Global Variables Are Forbidden

This is the most impactful change and the one that catches the most bugs.

Without strict mode:

function setName() {
userName = "Alice"; // No error (creates a global variable)
}
setName();
console.log(userName); // "Alice" (leaked to global scope)

With strict mode:

"use strict";

function setName() {
userName = "Alice"; // ReferenceError: userName is not defined
}
setName();

The fix is to always declare variables with let, const, or var:

"use strict";

function setName() {
let userName = "Alice"; // Properly declared
return userName;
}

2. Assignments to Read-Only Properties Throw Errors

Without strict mode:

let obj = {};
Object.defineProperty(obj, "name", {
value: "Alice",
writable: false // Cannot be changed
});

obj.name = "Bob"; // Silently fails (no error, no change)
console.log(obj.name); // "Alice"

With strict mode:

"use strict";

let obj = {};
Object.defineProperty(obj, "name", {
value: "Alice",
writable: false
});

obj.name = "Bob"; // TypeError: Cannot assign to read only property 'name'

3. Assignments to Non-Writable Globals Throw Errors

Without strict mode:

undefined = "surprise";   // Silently fails
NaN = 42; // Silently fails
Infinity = 0; // Silently fails
// No errors, but the assignments have no effect

With strict mode:

"use strict";

undefined = "surprise"; // TypeError: Cannot assign to read only property 'undefined'

4. Deleting Undeletable Properties Throws Errors

Without strict mode:

delete Object.prototype;        // Silently fails (returns false, no error)
console.log(Object.prototype); // Still exists

With strict mode:

"use strict";

delete Object.prototype; // TypeError: Cannot delete property 'prototype'

5. Deleting Variables and Functions Is Forbidden

Without strict mode:

var x = 10;
delete x; // Silently fails (returns false)
console.log(x); // 10

With strict mode:

"use strict";

var x = 10;
delete x; // SyntaxError: Delete of an unqualified identifier in strict mode

6. Duplicate Parameter Names Are Forbidden

Without strict mode:

function add(a, a, b) {     // Duplicate parameter 'a' (no error)
return a + b; // The second 'a' overwrites the first
}
console.log(add(1, 2, 3)); // 5 (a=2, b=3, first 'a' is lost)

With strict mode:

"use strict";

function add(a, a, b) { // SyntaxError: Duplicate parameter name not allowed
return a + b;
}

7. Octal Literal Syntax Is Forbidden

Legacy JavaScript allowed octal numbers with a leading zero, which caused confusion:

Without strict mode:

let filePermissions = 0755;     // Octal: actually 493 in decimal!
console.log(filePermissions); // 493 (probably not what the developer expected)

With strict mode:

"use strict";

let filePermissions = 0755; // SyntaxError: Octal literals are not allowed

// Use the explicit 0o prefix instead (ES6):
let filePermissions = 0o755; // Clear, intentional octal notation
console.log(filePermissions); // 493

8. Setting Properties on Primitives Throws Errors

Without strict mode:

let str = "hello";
str.customProp = "world"; // Silently fails
console.log(str.customProp); // undefined (the property was never saved)

let num = 42;
num.toFixed = "broken"; // Silently fails
console.log(num.toFixed(2)); // "42.00" (original method still works)

With strict mode:

"use strict";

let str = "hello";
str.customProp = "world"; // TypeError: Cannot create property 'customProp' on string 'hello'

9. The with Statement Is Forbidden

The with statement was a confusing feature that made it ambiguous which variable a name referred to:

Without strict mode:

let obj = { a: 1, b: 2, c: 3 };

with (obj) {
console.log(a + b + c); // 6 (but which 'a'? obj.a or a variable named 'a'?)
}

With strict mode:

"use strict";

with (obj) { // SyntaxError: Strict mode code may not include a with statement
console.log(a + b + c);
}

The with statement creates ambiguity that prevents the engine from optimizing code. Strict mode bans it entirely.

10. eval Cannot Introduce Variables into the Surrounding Scope

Without strict mode:

eval("var sneakyVariable = 42;");
console.log(sneakyVariable); // 42 (eval leaked a variable into this scope!)

With strict mode:

"use strict";

eval("var sneakyVariable = 42;");
console.log(sneakyVariable); // ReferenceError: sneakyVariable is not defined
// eval creates its own scope in strict mode

11. this in Functions Defaults to undefined Instead of window

This change is especially important and prevents accidental pollution of the global object.

Without strict mode:

function showThis() {
console.log(this); // Window object (in browsers)
}
showThis();

With strict mode:

"use strict";

function showThis() {
console.log(this); // undefined
}
showThis();

This catches a common bug where methods lose their context:

"use strict";

let user = {
name: "Alice",
greet() {
console.log(this.name);
}
};

let greetFn = user.greet;
greetFn();
// Strict mode: TypeError: Cannot read properties of undefined (reading 'name')
// Sloppy mode: undefined (reads window.name, which silently gives wrong result)

In strict mode, the error is caught immediately. In sloppy mode, the function silently accesses window.name, which returns an empty string or some unrelated value, producing a subtle bug.

12. Reserved Words for Future Use

Strict mode reserves additional keywords that may become part of the language in future versions:

"use strict";

let implements = 1; // SyntaxError
let interface = 2; // SyntaxError
let package = 3; // SyntaxError
let private = 4; // SyntaxError
let protected = 5; // SyntaxError
let public = 6; // SyntaxError
let static = 7; // SyntaxError (note: 'static' is now used in classes)

Complete Summary Table

BehaviorNormal ModeStrict Mode
Assign to undeclared variableCreates global variable silentlyReferenceError
Assign to read-only propertyFails silentlyTypeError
Assign to getter-only propertyFails silentlyTypeError
Delete undeletable propertyReturns false silentlyTypeError
Delete variable/functionReturns false silentlySyntaxError
Duplicate function parametersLast value winsSyntaxError
Octal literal 0777Interpreted as octalSyntaxError
Set property on primitiveFails silentlyTypeError
with statementAllowedSyntaxError
eval leaks variablesLeaks to outer scopeContained in eval scope
this in plain function callwindow / globalundefined
arguments.calleeReturns current functionTypeError
Future reserved words as namesAllowedSyntaxError

Should You Always Use Strict Mode?

The short answer: you probably already are using it without realizing it.

ES Modules Are Always Strict

If you use import and export (ES modules), your code automatically runs in strict mode. There is no way to opt out.

// app.js (loaded as a module)
// "use strict" is NOT needed here (it's automatic)

import { getUser } from "./user.js";

mistke = 10; // ReferenceError (strict mode is already active)

In HTML:

<!-- type="module" enables strict mode automatically -->
<script type="module" src="app.js"></script>

Classes Are Always Strict

All code inside a class body runs in strict mode automatically:

// No "use strict" directive needed
class Calculator {
add(a, a) { // SyntaxError: Duplicate parameter name
return a + a;
}
}

When You Still Need "use strict" Explicitly

You need to add "use strict"; manually when:

  1. Writing plain <script> tags (not type="module"):
<script>
"use strict";
// Your code here
</script>
  1. Writing Node.js CommonJS files (using require instead of import):
"use strict";

const fs = require("fs");
// Your code here
  1. Working on legacy codebases that do not use modules or bundlers

The Modern Reality

In 2024 and beyond, most JavaScript is written using one or more of the following:

  • ES modules (import/export) which are strict by default
  • Bundlers (Webpack, Vite, Rollup) that compile code as modules
  • Frameworks (React, Vue, Angular) that use modules throughout
  • TypeScript which compiles to strict-mode JavaScript

In these environments, you rarely need to write "use strict"; yourself. The tooling handles it. However, understanding what strict mode does remains essential because you will encounter its error messages, and you need to know why they occur.

tip

Practical rule: If you are writing a standalone .js file loaded with a regular <script> tag (no type="module"), add "use strict"; at the top. In every other modern setup, it is already active.

Common Mistake: Forgetting Strict Mode and Silent Errors

Let us look at real-world scenarios where the absence of strict mode causes bugs that are extremely difficult to track down.

Scenario 1: The Invisible Typo

A developer writes a shopping cart function with a subtle typo:

// No strict mode

let totalPrice = 0;

function addItem(price, quantity) {
totalPrce = price * quantity; // Typo: 'totalPrce' instead of 'totalPrice'
}

addItem(29.99, 3);
console.log(totalPrice); // 0 (unchanged!)
console.log(totalPrce); // 89.97 (exists as a global variable)

// The developer sees totalPrice is 0 and spends 30 minutes
// wondering why addItem() "doesn't work"

With strict mode, the bug surfaces immediately:

"use strict";

let totalPrice = 0;

function addItem(price, quantity) {
totalPrce = price * quantity; // ReferenceError: totalPrce is not defined
}

The error message even tells you the exact line where the problem is.

Scenario 2: The Silently Frozen Object

// No strict mode

const config = Object.freeze({
apiUrl: "https://api.example.com",
timeout: 5000
});

// A colleague changes the timeout for debugging
config.timeout = 30000;

// Later, another developer wonders why the timeout is still 5 seconds
console.log(config.timeout); // 5000 (the assignment silently failed)

With strict mode:

"use strict";

const config = Object.freeze({
apiUrl: "https://api.example.com",
timeout: 5000
});

config.timeout = 30000; // TypeError: Cannot assign to read only property 'timeout'

Scenario 3: The Lost this Context

// No strict mode

class UserService {
constructor() {
this.users = ["Alice", "Bob"];
}

getUsers() {
return this.users;
}
}

const service = new UserService();
const getUsers = service.getUsers;

// Passing method as callback loses 'this'
console.log(getUsers());
// Sloppy mode: TypeError only if window.users doesn't exist
// OR returns window.users if it does exist (wrong data, no error)

// Classes are strict by default, so this actually throws:
// TypeError: Cannot read properties of undefined (reading 'users')
// But in a plain object without strict mode, it would silently return wrong data

Scenario 4: The Directive That Never Activated

This is perhaps the most insidious mistake because there is no error at all:

// Developer intended to enable strict mode, but it's not the first statement

console.log("App starting...");
"use strict"; // This is just a string expression! Strict mode is NOT enabled.

accidentalGlobal = "oops"; // No error (strict mode never activated)

The fix:

"use strict";
console.log("App starting...");

accidentalGlobal = "oops"; // ReferenceError (strict mode is properly active)

Checking If Strict Mode Is Active

If you are ever unsure whether strict mode is enabled, you can test it:

function isStrictMode() {
try {
undeclaredVar = 42; // This will throw in strict mode
delete undeclaredVar; // Clean up if we're in sloppy mode
return false;
} catch (e) {
return true;
}
}

console.log(isStrictMode()); // true or false

A simpler and more reliable way: check this in a plain function call.

function checkStrict() {
return !this; // In strict mode, 'this' is undefined (falsy)
// In sloppy mode, 'this' is window (truthy)
}

console.log(checkStrict()); // true if strict, false if sloppy

Summary

Strict mode is JavaScript's built-in safety net that transforms silent failures into actionable errors:

  • Enable it with "use strict"; as the very first statement of a script or function.
  • It catches accidental globals, assignments to read-only properties, duplicate parameters, and many other common mistakes.
  • this in plain function calls becomes undefined instead of the global object, catching context-loss bugs immediately.
  • The with statement and legacy octal syntax are forbidden.
  • eval is sandboxed and cannot leak variables into the surrounding scope.
  • ES modules (import/export) and class bodies are always in strict mode automatically.
  • In modern projects using modules, bundlers, or frameworks, strict mode is typically already active without needing the explicit directive.
  • When writing standalone scripts or Node.js CommonJS files, always add "use strict"; at the top.

There is no downside to strict mode. It never makes correct code stop working. It only reveals errors that were previously hidden. Every modern JavaScript best practice assumes strict mode is active.