Productivity

JavaScript Optional Chaining (?.): Never Crash on Null Again

📅 Updated: December 2025 ⏱️ 6 min read 💻 JavaScript/TypeScript

"Cannot read property 'x' of undefined"—every JavaScript developer has seen this error hundreds of times. Optional chaining (?.) eliminates this entire class of bugs with a simple syntax change. It's available in all modern browsers and Node.js, yet many developers don't use it consistently.

Start using ?. everywhere and watch your runtime errors drop dramatically.

The Problem

// This crashes if user or address is undefined
const street = user.address.street;

// Traditional defensive code is verbose
const street = user && user.address && user.address.street;

// Or with ternary (even worse)
const street = user ? (user.address ? user.address.street : undefined) : undefined;

The Solution: Optional Chaining

// Clean and safe
const street = user?.address?.street;

// Returns undefined if any part of the chain is null/undefined
// Never throws an error

💡 How It Works

?. checks if the value before it is null or undefined. If so, it short-circuits and returns undefined instead of trying to access the property. The entire remaining chain is skipped.

Syntax Variations

Property Access

// Object properties
const name = user?.profile?.name;

// Nested objects
const city = response?.data?.user?.address?.city;

Array Access

// Array elements
const firstItem = arr?.[0];
const nested = data?.items?.[0]?.name;

Method Calls

// Safe method calls
const result = obj?.method?.();

// Callbacks
props.onClick?.();

// With arguments
user?.sendEmail?.("Hello");

Dynamic Properties

// Bracket notation
const value = obj?.[dynamicKey];
const prop = obj?.[computeKey()];

Combine with Nullish Coalescing (??)

Optional chaining returns undefined when short-circuiting. Use ?? to provide default values:

// Default value if any part is null/undefined
const name = user?.profile?.name ?? "Anonymous";

// Default for array
const first = items?.[0] ?? "No items";

// Default for method result
const count = obj?.getCount?.() ?? 0;

?? vs ||

// || treats 0, "", and false as falsy
const count = data.count || 10;  // Returns 10 if count is 0

// ?? only triggers on null/undefined
const count = data.count ?? 10;  // Returns 0 if count is 0

Common Patterns

API Response Handling

// Safe API data extraction
const users = response?.data?.users ?? [];
const error = response?.error?.message ?? "Unknown error";

React Props

// Safe prop access
const userName = props.user?.name;
const handleClick = props.onClick?.bind(this);

// In JSX
{user?.profile?.avatar && }

Event Handling

// Safe event property access
const value = event?.target?.value ?? "";
const key = event?.key?.toLowerCase();

Configuration Objects

// Safe config access with defaults
const timeout = config?.settings?.timeout ?? 5000;
const debug = config?.flags?.debug ?? false;

⚠️ Don't Overuse

Optional chaining shouldn't replace proper validation. If a value should exist, validate it explicitly and throw a meaningful error rather than silently returning undefined.

TypeScript Integration

Optional chaining works perfectly with TypeScript and helps with type narrowing:

interface User {
  profile?: {
    name?: string;
    email: string;
  };
}

// TypeScript knows name might be undefined
const name = user?.profile?.name;  // string | undefined

// With default
const name = user?.profile?.name ?? "Guest";  // string

Browser/Node Support

  • Chrome: 80+
  • Firefox: 74+
  • Safari: 13.1+
  • Edge: 80+
  • Node.js: 14+

For older environments, use Babel to transpile.

ESLint Rule

Enforce optional chaining in your codebase:

// .eslintrc
{
  "rules": {
    "prefer-optional-chain": "error"
  }
}

Conclusion

Optional chaining is one of those features that should become muscle memory. Every time you access a property that might not exist, use ?.. Every time you need a default value, combine it with ??.

The small syntax change eliminates an entire category of runtime errors and makes your code more resilient to unexpected data shapes.