How to Use the JavaScript Switch Case Full Guide
Learn the complete JavaScript switch statement syntax including case labels, break, default, fall-through, grouped cases, and real-world patterns. Covers every switch feature with practical code examples.
The switch statement is JavaScript's built-in tool for matching one value against multiple possible cases. When you have a variable that could be "pending", "shipped", "delivered", or "cancelled", and each status needs different handling, a switch statement expresses that logic more clearly than a chain of else if comparisons. Instead of repeating the variable name in every condition, you write it once and let the switch structure match it against labeled cases.
This guide covers the complete switch syntax, explains how break and default work, demonstrates fall-through behavior (both intentional and accidental), shows grouped cases for shared logic, and walks through real-world patterns like command dispatchers and state machines.
Switch Statement Syntax
The switch statement evaluates an expression once, then compares its result against each case label using strict equality (===):
switch (expression) {
case value1:
// Runs when expression === value1
break;
case value2:
// Runs when expression === value2
break;
case value3:
// Runs when expression === value3
break;
default:
// Runs when no case matches
}Each piece has a specific role:
| Part | Purpose | Required |
|---|---|---|
switch (expression) | The value being compared | Yes |
case value: | A value to compare against | At least one |
break; | Stops execution from falling through to the next case | Strongly recommended |
default: | Handles unmatched values (like else) | Recommended |
const fruit = "apple";
switch (fruit) {
case "apple":
console.log("Apples are $1.50/lb");
break;
case "banana":
console.log("Bananas are $0.75/lb");
break;
case "orange":
console.log("Oranges are $2.00/lb");
break;
default:
console.log("We don't carry that fruit");
}
// Output: "Apples are $1.50/lb"How Break Works
The break statement exits the switch block immediately. Without break, JavaScript continues executing the next case's code regardless of whether it matches. This is called fall-through:
const color = "red";
// WITHOUT break (fall-through happens)
switch (color) {
case "red":
console.log("Red selected");
// No break! Execution continues...
case "blue":
console.log("Blue selected");
// No break! Still continuing...
case "green":
console.log("Green selected");
break;
default:
console.log("Unknown color");
}
// Output:
// "Red selected"
// "Blue selected" ← Unintended!
// "Green selected" ← Unintended!// WITH break (correct behavior)
switch (color) {
case "red":
console.log("Red selected");
break; // Stops here
case "blue":
console.log("Blue selected");
break;
case "green":
console.log("Green selected");
break;
default:
console.log("Unknown color");
}
// Output: "Red selected" (only)Always Add Break to Every Case
Missing break is one of the most common switch bugs. It causes multiple cases to execute silently. Unless you intentionally want fall-through behavior, every case should end with break or return. ESLint's no-fallthrough rule catches missing breaks automatically.
The Default Case
The default label runs when no case value matches the switch expression. It works like the else block in an if/else chain:
function getFileIcon(extension) {
switch (extension) {
case ".js":
return "📜";
case ".ts":
return "🔷";
case ".py":
return "🐍";
case ".rs":
return "🦀";
case ".go":
return "🐹";
default:
return "📄"; // Generic file icon for unknown extensions
}
}
console.log(getFileIcon(".js")); // "📜"
console.log(getFileIcon(".rs")); // "🦀"
console.log(getFileIcon(".cpp")); // "📄" (matched default)The default case can appear anywhere in the switch block, but convention places it last. If placed elsewhere, it still only runs when no other case matches (unless fall-through from above reaches it).
Grouped Cases (Shared Logic)
When multiple values need the same code, stack case labels without break between them:
function getQuarter(month) {
switch (month) {
case "January":
case "February":
case "March":
return "Q1";
case "April":
case "May":
case "June":
return "Q2";
case "July":
case "August":
case "September":
return "Q3";
case "October":
case "November":
case "December":
return "Q4";
default:
return "Invalid month";
}
}
console.log(getQuarter("March")); // "Q1"
console.log(getQuarter("November")); // "Q4"This is the one scenario where intentional fall-through is standard practice. Each empty case label falls through to the one below it, and the final case in the group has the shared logic.
Switch with Return (No Break Needed)
When the switch lives inside a function and every case uses return, you do not need break because return exits the function entirely:
function getHttpMethod(action) {
switch (action) {
case "create":
return "POST";
case "read":
return "GET";
case "update":
return "PUT";
case "patch":
return "PATCH";
case "delete":
return "DELETE";
default:
return "GET";
}
}This is the cleanest switch pattern because there is no break clutter and no fall-through risk.
Real-World Switch Patterns
State Machine for Order Processing
function processOrderTransition(currentState, action) {
switch (currentState) {
case "draft":
switch (action) {
case "submit": return "pending";
case "delete": return "deleted";
default: return currentState;
}
case "pending":
switch (action) {
case "approve": return "processing";
case "reject": return "draft";
case "cancel": return "cancelled";
default: return currentState;
}
case "processing":
switch (action) {
case "ship": return "shipped";
case "cancel": return "cancelled";
default: return currentState;
}
case "shipped":
switch (action) {
case "deliver": return "delivered";
case "return": return "returned";
default: return currentState;
}
default:
return currentState; // Terminal states (delivered, cancelled, etc.)
}
}
console.log(processOrderTransition("draft", "submit")); // "pending"
console.log(processOrderTransition("pending", "approve")); // "processing"
console.log(processOrderTransition("shipped", "deliver")); // "delivered"
console.log(processOrderTransition("delivered", "ship")); // "delivered" (no valid transition)Keyboard Shortcut Handler
function handleShortcut(event) {
if (!event.ctrlKey && !event.metaKey) return;
switch (event.key) {
case "s":
event.preventDefault();
saveDocument();
break;
case "z":
event.preventDefault();
if (event.shiftKey) {
redo();
} else {
undo();
}
break;
case "c":
copySelection();
break;
case "v":
pasteClipboard();
break;
case "f":
event.preventDefault();
openSearchPanel();
break;
default:
break; // Unhandled shortcut
}
}Formatting Values by Type
function formatValue(value) {
switch (typeof value) {
case "string":
return `"${value}"`;
case "number":
return Number.isInteger(value) ? String(value) : value.toFixed(2);
case "boolean":
return value ? "true" : "false";
case "undefined":
return "undefined";
case "object":
if (value === null) return "null";
if (Array.isArray(value)) return `[${value.length} items]`;
return `{${Object.keys(value).length} keys}`;
case "function":
return `fn:${value.name || "anonymous"}`;
default:
return String(value);
}
}
console.log(formatValue("hello")); // '"hello"'
console.log(formatValue(3.14159)); // "3.14"
console.log(formatValue(true)); // "true"
console.log(formatValue([1, 2, 3])); // "[3 items]"
console.log(formatValue({ a: 1 })); // "{1 keys}"Block Scoping Inside Cases
Variables declared with let or const inside a case clause are accessible to all cases in the switch block unless you wrap the case in its own block:
// Problem: variable collision
switch (action) {
case "create":
const message = "Created!"; // Declared here
break;
case "delete":
const message = "Deleted!"; // ERROR: 'message' already declared
break;
}
// Solution: wrap each case in a block
switch (action) {
case "create": {
const message = "Created!";
console.log(message);
break;
}
case "delete": {
const message = "Deleted!"; // No conflict — different block scope
console.log(message);
break;
}
}Use Blocks for Variable Declarations
When any case declares variables with let or const, wrap that case body in curly braces { } to create a separate scope. Without braces, all cases share the same scope and you get "identifier already declared" errors.
Switch Comparison: Strict Equality Only
Switch uses === for comparisons, which means type matters:
const input = "5"; // String, not number
switch (input) {
case 5:
console.log("Matched number 5");
break;
case "5":
console.log("Matched string '5'");
break;
}
// Output: "Matched string '5'" (=== compares type and value)| Switch Expression | Case Label | Matches? | Reason |
|---|---|---|---|
5 (number) | case "5": | No | Different types |
"5" (string) | case 5: | No | Different types |
null | case undefined: | No | Different values |
true | case 1: | No | Different types |
0 | case false: | No | Different types |
Best Practices
Clean Switch Statements
Follow these conventions for readable and maintainable switch blocks.
Always include a default case. Even if you believe all possible values are covered, add a default to handle unexpected inputs. Log an error or return a sensible fallback.
Use return instead of break in functions. When a switch is inside a function and each case returns a value, return is cleaner than break because it eliminates fall-through risk entirely.
Wrap cases with variable declarations in blocks. Add { } around any case that uses let, const, or class to prevent scope collisions between cases.
Keep case bodies short. If a case requires more than 5-6 lines of logic, extract that logic into a named function. The switch should dispatch to functions, not contain business logic inline.
Comment intentional fall-through. When you intentionally omit break for fall-through (beyond grouped empty cases), add // falls through so other developers know it is deliberate.
Common Mistakes and How to Avoid Them
Switch Statement Pitfalls
These mistakes are responsible for most switch-related bugs.
Forgetting break statements. The most common switch bug. Without break, execution falls through to the next case. Use return when possible, or enable ESLint's no-fallthrough rule.
Type mismatches between expression and cases. Switch uses ===, so switch (userInput) where userInput is a string will not match case 42 (a number). Ensure consistent types.
Declaring variables without block scope. Writing const x = 1; inside a case without braces shares x with all other cases, causing redeclaration errors. Always use { } when declaring variables.
Using switch for range-based conditions. Switch is designed for exact value matching. The switch (true) hack for ranges is confusing and should be replaced with if/else chains.
Putting default in the middle without understanding fall-through. The default case can appear anywhere, but if placed in the middle without break, it falls through to the case below it. Keep default at the end to avoid confusion.
Next Steps
Combine switch with other control flow
Use switch inside loops to process arrays of commands, events, or state transitions.
Learn the [ternary operator](/tutorials/programming-languages/javascript/javascript-ternary-operator-complete-syntax-guide)
For simple conditional assignments, the ternary operator provides an even more compact alternative to both switch and if/else.
Explore object lookups as alternatives
When every switch case maps a key to a value, an object lookup is shorter and more extensible.
Build a state machine
Practice switch patterns by building a simple state machine for a UI wizard, a game, or an order processing pipeline.
Rune AI
Key Insights
- Break prevents fall-through: every case needs
breakorreturnto stop execution from running into the next case - Strict equality only: switch uses
===, so type mismatches between expression and case values cause silent failures - Block scope with braces: wrap any case that declares
letorconstvariables in{ }to prevent scope collisions - Default is your safety net: always include a default case to handle unexpected values explicitly
Frequently Asked Questions
Does the JavaScript switch statement use strict or loose equality?
What happens if I forget the break statement in a switch?
Can I use switch with strings in JavaScript?
Is the default case required in a switch statement?
Can I put a switch inside another switch?
Conclusion
The switch statement is JavaScript's cleanest syntax for matching one value against many exact cases. Its three essential rules are: always include break or return in every case, always include a default case, and use block scoping when declaring variables inside cases. For pure value mappings with no logic, consider an object lookup as a cleaner alternative. For range-based or multi-variable conditions, stick with if/else chains.
Tags
More in this topic
OffscreenCanvas API in JS for UI Performance
Master the OffscreenCanvas API to offload rendering from the main thread. Covers worker-based 2D and WebGL rendering, animation loops inside workers, bitmap transfer, double buffering, chart rendering pipelines, image processing, and performance measurement strategies.
Advanced Web Workers for High Performance JS
Master Web Workers for truly parallel JavaScript execution. Covers dedicated and shared workers, structured cloning, transferable objects, SharedArrayBuffer with Atomics, worker pools, task scheduling, Comlink RPC patterns, module workers, and performance profiling strategies.
JavaScript Macros and Abstract Code Generation
Master JavaScript code generation techniques for compile-time and runtime metaprogramming. Covers AST manipulation, Babel plugin authorship, tagged template literals as macros, code generation pipelines, source-to-source transformation, compile-time evaluation, and safe eval alternatives.