Skip to main content Accessibility Feedback

Optional chaining in vanilla JS

In vanilla JS, if you try to run a method on an element or object that doesn’t exist the browser throws an error. The same thing happens if you try to access a nested object property that doesn’t exist.

// If an element with the class .harry-potter doesn't exist, the browser throws this error:
// Uncaught TypeError: Cannot read property 'classList' of null
document.querySelector('.harry-potter').classList.add('meh');

// If harry isn't a property of wizards, or house isn't a property of harry, the browser throws this error:
// Uncaught TypeError: Cannot read property 'house' of undefined
var wizards = {};
wizards.harry.house.toLowerCase();

For debugging purposes, the error and hard failure is really useful. But it can make writing resilient code that doesn’t break if the thing you’re looking for doesn’t exist a lot harder.

Let’s look at how to fix this.

The old-school way to handle this

Historically, you’ve had to check for the thing you’re trying to manipulate before trying to run your chained code.

// Get an element with the class .harry-potter
var harry = document.querySelector('.harry-potter');

// If an element with the class .harry-potter exists, add the .meh class
if (harry) {
	harry.classList.add('meh');
}

// If the wizards.harry.house property exists, convert it to lower case
if (wizards.harry && wizards.harry.house) {
	wizards.harry.house.toLowerCase();
}

This works, but it results in verbose code and it’s really easy to forget a check and cause an error.

Fortunately, there’s a newer, simpler way to handle this.

Optional chaining

Optional chaining is a browser-native way to chain methods or properties, and conditionally continue down the chain if the value is not null or undefined.

To use optional chaining, add a question mark (?) before the dot (.) in your chain.

// If an element with the class .harry-potter doesn't exist, nothing happens
document.querySelector('.harry-potter')?.classList.add('meh');

// If harry isn't a property of wizards, or house isn't a property of harry, nothing happens
var wizards = {};
wizards.harry?.house.toLowerCase();

You can also combine optional chaining with something called a nullish coalescing operator (??, more on that tomorrow) to conditionally use an alternate value if the item doesn’t exist.

// If wizards.harry.house doesn't exist, use "hufflepuff" instead
var house = wizards.harry?.house.toLowerCase() ?? 'hufflepuff';

Browser support

Optional chaining works in all modern browsers, but unfortunately has no IE support and cannot be polyfilled.