JavaScript gives you four ways to make a decision on one line: the ternary operator, &&, || and ??. Each is genuinely useful in a specific place, and each becomes unreadable when pushed past it. This guide shows where each earns its place and where a plain if is the better answer.
The ternary operator#
const status = age >= 18 ? "adult" : "minor";
Read it as: condition, then the value if true, then the value if false. The important difference from an if statement is that this is an expression — it produces a value, so it can go anywhere a value can go.
// In a template string
console.log(`You have ${count} item${count === 1 ? "" : "s"}`);
// As a function argument
setTimeout(save, isUrgent ? 0 : 5000);
// In an object literal
const config = {
mode: isDev ? "development" : "production",
};
// As a return value
const label = (n) => n < 0 ? "negative" : n === 0 ? "zero" : "positive";
That last one is a chained ternary. Two levels reads fine when the values line up; three or more should be a switch or a lookup object.
The logical AND operator#
&& stops as soon as it finds something falsy and returns it; otherwise it returns the last value. That makes it a compact “only if” :
// Only calls the function if it exists
onSuccess && onSuccess(result);
// Only reads the property if the object exists
const city = user && user.address && user.address.city;
It is used constantly in JSX for conditional rendering:
{errors.length > 0 && <ErrorList items={errors} />}
OR for defaults, and where it goes wrong#
const name = input || "Anonymous";
This returns the first truthy value. The problem is that JavaScript treats several legitimate values as falsy:
const count = 0 || 10; // 10 - wrong, 0 was a real value
const label = "" || "Untitled"; // "Untitled" - maybe wrong
const flag = false || true; // true - definitely wrong
If zero, an empty string or false are valid values in your data, || silently replaces them.
Nullish coalescing#
?? only falls back when the left side is null or undefined:
const count = 0 ?? 10; // 0 - correct
const label = "" ?? "Untitled"; // "" - correct
const missing = undefined ?? 10; // 10 - correct
The rule is simple: use ?? for defaults, and || only when you genuinely want every falsy value replaced.
// Settings, where 0 and false are meaningful
const timeout = settings.timeout ?? 3000;
const isEnabled = settings.enabled ?? true;
const volume = settings.volume ?? 0.5;
You cannot mix ?? with && or || without brackets — that is a syntax error on purpose, because the precedence would be ambiguous:
const value = a || b ?? c; // SyntaxError
const value = (a || b) ?? c; // fine
Optional chaining#
?. replaces the long && chains for property access:
const city = user?.address?.city; // undefined if any step is missing
const first = list?.[0]; // safe indexing
const result = callbacks.onDone?.(); // only calls if defined
// Combines naturally with ??
const city = user?.address?.city ?? "Unknown";
When to stop and write a normal if#
Three practical signals:
- The line no longer fits comfortably on screen. Wrapping a ternary across four lines removes the only advantage it had.
- The branches do things rather than produce values. That is what statements are for.
- You had to think about precedence. If you needed brackets to be sure, the reader will need them too.
// Hard to follow
const result = a ? (b ? x : y) : (c ? z : w);
// Clearer
let result;
if (!a) {
result = c ? z : w;
} else {
result = b ? x : y;
}
For several discrete cases, a lookup object beats both:
const MESSAGES = {
404: "Not found",
403: "Not allowed",
500: "Something went wrong",
};
const message = MESSAGES[status] ?? "Unexpected error";
Questions people ask#
Is the ternary operator slower than an if?
No. They compile to effectively the same thing. Choose on readability alone.
Can I use await inside a ternary?
Yes, inside an async function: const data = useCache ? cached : await fetchData();. It is legal, though an if is often clearer when one branch does real work.
What is the difference between ?? and ||= ?
?? chooses a value. ??= assigns only when the current value is null or undefined: options.retries ??= 3. There are matching ||= and &&= operators.
Does every browser support ?? and ?.
Every current browser does. If you must support older environments, a build step such as Babel or TypeScript converts them for you.
Where to go next#
- JavaScript array methods reference — where these operators show up most.
- The JavaScript fetch API — optional chaining on API responses.
- Why is my JavaScript not working? — when undefined turns up unexpectedly.