Optional chaining (?.) lets you safely access deeply nested object properties, methods, or array elements without throwing an error if something in the chain is null or undefined. Instead of a runtime error, the expression evaluates to undefined.
It is especially useful when working with API responses or complex objects where some properties might be missing.
obj?.prop to access a property only if the object exists.obj?.nested?.prop to avoid long chains of checks.obj.method?.() to call a method only if it exists.arr?.[index] to access an element only if the array exists.null or undefined is found in the chain, the whole expression returns undefined.obj?.prop – safely access prop of obj.obj?.[expr] – safely access a dynamic property using an expression.obj.method?.() – safely call method() if it exists.Optional chaining is read-only – it does not create properties or modify objects. It only affects how JavaScript reads values from them.
const user = {
name: 'Alice',
address: { city: 'London' }
};
Accessing existing property → 'Alice'
console.log(user?.name);
Accessing nested property safely → 'London'
console.log(user?.address?.city);
Accessing non-existent property → undefined (no error)
console.log(user?.contact?.email);
const user = {
name: 'Bob',
greet: () => 'Hello!'
};
Call greet only if it exists
console.log(user.greet?.());
sayBye does not exist → returns undefined, no error
console.log(user.sayBye?.());
const users = [
{ name: 'Sam' },
{ name: 'Lara' }
];
Valid index → 'Sam'
console.log(users?.[0]?.name);
Out-of-range index → undefined (no error)
console.log(users?.[5]?.name);
const response = null;
Safely reading deeply nested data from a nullable response
console.log(response?.data?.items?.[0]);
const settings = {
theme: {
color: 'dark'
}
};
If theme or color is missing, use 'light' as default
const themeColor = settings?.theme?.color ?? 'light';
console.log(themeColor);
In the basic example:
user?.name returns 'Alice' because user exists and has name.user?.address?.city returns 'London' because both address and city exist.user?.contact?.email returns undefined because contact is undefined, but no error is thrown.In the function example, user.greet?.() calls the function and returns 'Hello!', while user.sayBye?.() simply evaluates to undefined because sayBye is not defined.
With arrays, users?.[5]?.name is safe even though index 5 does not exist. Without optional chaining, directly accessing users[5].name would cause a runtime error.
??) to provide safe default values.if checks to keep your code clean and readable.undefined.user.profile.settings) and safely access a deeply nested property using optional chaining.onLogin) to an object. Call it using obj.onLogin?.() and provide a fallback message using ??.arr?.[index]?.name, including out-of-range indices.