Knowledge Base/concepts/Default Parameters in JavaScript

Default Parameters in JavaScript

Default parameters in JavaScript are not a prettier version of || fallbacks. They are more specific than that: JavaScript uses the default only when the argument is missing or explicitly undefined. That small rule is why default parameters are usually the right tool for optional function inputs, and also why they do not magically clean up every empty value a caller can send.

Use default parameters when a function has a normal fallback for an omitted input. Use explicit checks when null, "", 0, or false should also be replaced.

The basic syntax

A default parameter puts the fallback value in the function signature:

function greetLearner(name = "friend") {
  return `Hello, ${name}!`;
}

console.log(greetLearner("Mira")); // "Hello, Mira!"
console.log(greetLearner()); // "Hello, friend!"

The default belongs next to the input it describes. That makes the function contract visible before anyone reads the body.

You can use defaults with function declarations, function expressions, and arrow functions:

const formatReminder = (title, minutesBefore = 10) => {
  return `${title}: ${minutesBefore} minutes before`;
};

console.log(formatReminder("Review")); // "Review: 10 minutes before"
console.log(formatReminder("Standup", 5)); // "Standup: 5 minutes before"

For more on the arrow syntax side, read JavaScript Arrow Functions.

Defaults only run for undefined

This is the main rule:

function label(value = "untitled") {
  return value;
}

console.log(label()); // "untitled"
console.log(label(undefined)); // "untitled"
console.log(label(null)); // null
console.log(label("")); // ""
console.log(label(false)); // false
console.log(label(0)); // 0

null, an empty string, false, and 0 are real values. JavaScript does not treat them as missing arguments.

That is different from a truthy/falsy fallback:

function labelWithOr(value) {
  return value || "untitled";
}

console.log(labelWithOr("")); // "untitled"
console.log(labelWithOr(0)); // "untitled"

Sometimes that is what you want. Often it is a bug with a good disguise. If 0 is a valid quantity, false is a valid setting, or "" is a deliberate empty label, || will erase the caller's choice.

For the underlying truthy and falsy rules, see Truthy and Falsey Values in JavaScript.

The naive fallback that changes valid input

Before default parameters, you often saw this pattern:

function createPage(title, pageNumber) {
  pageNumber = pageNumber || 1;
  return `${title} - page ${pageNumber}`;
}

It works for a missing page number:

console.log(createPage("Intro")); // "Intro - page 1"

Then someone passes 0 because they are rendering a zero-based preview page:

console.log(createPage("Intro", 0)); // "Intro - page 1"

The function silently replaced a valid argument. A default parameter keeps the fallback narrower:

function createPage(title, pageNumber = 1) {
  return `${title} - page ${pageNumber}`;
}

console.log(createPage("Intro", 0)); // "Intro - page 0"
console.log(createPage("Intro")); // "Intro - page 1"

That is the real value of default parameters. They describe "missing" without pretending every falsy value is missing.

Defaults are evaluated when the function is called

Default expressions run during each call that needs them:

function makeTicket(prefix = "TICKET", id = crypto.randomUUID()) {
  return `${prefix}-${id}`;
}

If the caller provides id, the default expression is not evaluated:

console.log(makeTicket("BUG", "42")); // "BUG-42"

You can prove the call-time behavior with a small counter:

let nextId = 1;

function createTask(title, id = nextId++) {
  return { id, title };
}

console.log(createTask("Write tests")); // { id: 1, title: "Write tests" }
console.log(createTask("Refactor")); // { id: 2, title: "Refactor" }
console.log(createTask("Deploy", 99)); // { id: 99, title: "Deploy" }
console.log(nextId); // 3

The third call does not increment nextId because id was supplied.

Later defaults can use earlier parameters

Default parameters are evaluated from left to right. A later default can read an earlier parameter:

function buildFileName(name, extension = "txt", fullName = `${name}.${extension}`) {
  return fullName;
}

console.log(buildFileName("notes")); // "notes.txt"
console.log(buildFileName("notes", "md")); // "notes.md"

Do not read a later parameter from an earlier default:

function broken(name = fallback, fallback = "guest") {
  return name;
}

broken();
// ReferenceError: Cannot access 'fallback' before initialization

JavaScript has not initialized fallback yet. Parameter order still matters.

That is the same practical lesson as ordinary argument order: callers and functions match values by position unless you choose an object shape instead.

Optional parameters usually belong at the end

Default parameters are easiest to call when optional inputs come after required ones:

function calculateTotal(price, quantity = 1) {
  return price * quantity;
}

console.log(calculateTotal(12)); // 12
console.log(calculateTotal(12, 3)); // 36

If a middle parameter has a default, callers need to pass undefined to skip it:

function formatUser(name, role = "member", active) {
  return `${name} is ${active ? "active" : "inactive"} as ${role}`;
}

console.log(formatUser("Ada", undefined, true));
// "Ada is active as member"

That call is legal, but it is not lovely. When you have several optional settings, use an options object.

Defaults with options objects

Options objects make optional values clearer because callers can name the values they want to override:

function createButton(label, options = {}) {
  const { variant = "primary", disabled = false } = options;

  return {
    label,
    variant,
    disabled,
  };
}

console.log(createButton("Save"));
// { label: "Save", variant: "primary", disabled: false }

console.log(createButton("Delete", { variant: "danger" }));
// { label: "Delete", variant: "danger", disabled: false }

There are two levels of defaults here:

  • options = {} handles the whole missing object.
  • variant = "primary" and disabled = false handle missing properties.

Without options = {}, this call would fail:

function brokenCreateButton(label, options) {
  const { variant = "primary" } = options;
  return `${label}: ${variant}`;
}

brokenCreateButton("Save");
// TypeError: Cannot destructure property 'variant' of 'options' as it is undefined.

If null should also mean "use the empty options object", default parameters are not enough. Normalize it before destructuring:

function createButton(label, options = {}) {
  const safeOptions = options ?? {};
  const { variant = "primary" } = safeOptions;
  return `${label}: ${variant}`;
}

?? replaces only null and undefined, which is usually better than || when false, 0, or "" might be valid.

When not to use a default parameter

Do not use a default parameter when the function should reject missing input:

function sendPasswordReset(email = "") {
  // This hides a bad call.
}

An email address is not optional just because an empty string keeps the function from crashing. Make required data required, and fail loudly when the caller breaks the contract:

function sendPasswordReset(email) {
  if (!email) {
    throw new Error("email is required");
  }

  return `Sending reset to ${email}`;
}

Defaults are for honest fallbacks, not for quietly absorbing invalid calls.

Key Takeaways

  • Default parameters run only when an argument is missing or undefined.
  • They preserve valid falsy values like 0, false, and "", unlike many || fallback patterns.
  • Default expressions are evaluated at call time and only when needed.
  • Later defaults can read earlier parameters, but not the other way around.
  • Use options objects when several optional settings make positional arguments awkward.
Share this article:
javascriptfundamentalsfunctions