What This Error Means
The full error is typically: TypeError: Cannot read properties of undefined (reading 'X') or the older form: TypeError: Cannot read property 'X' of null. Both mean the same thing: you tried to access a property on a value that is undefined or null, which have no properties.
The error in action:
const user = undefined; console.log(user.name); // TypeError: Cannot read properties of undefined (reading 'name') const data = null; console.log(data.items.map(x => x.id)); // TypeError: Cannot read properties of null (reading 'items')
The property name in parentheses is the first property that failed — not necessarily the deepest one in your chain. Read the stack trace to find exactly which line triggered it.
Fix 1: Optional Chaining (?.) — The Modern Solution
Optional chaining (?.) short-circuits and returns undefined instead of throwing when the left side is null or undefined. It works with property access, method calls, and bracket notation.
Without optional chaining — crashes on undefined:
const user = { address: undefined };
console.log(user.address.city); // ❌ TypeError
console.log(user.profile.avatar); // ❌ TypeError
user.save(); // ❌ TypeError if save doesn't existWith optional chaining — returns undefined safely:
// ✅ Property chains const city = user?.address?.city; // undefined, no crash const avatar = user?.profile?.avatar; // undefined, no crash // ✅ Method calls user?.save(); // skipped if save doesn't exist // ✅ Array / bracket access const first = items?.[0]?.name; // undefined if items is empty // ✅ Deep chain const zip = response?.data?.user?.address?.zip;
Fix 2: Default Values — Nullish Coalescing (??)
Combine optional chaining with the nullish coalescing operator ?? to provide a fallback when the value is null or undefined. Unlike ||, it does not fall through for falsy values like 0 or "".
OR operator swallows valid falsy values:
const count = apiData.count || 10; // ❌ If count is 0, this gives 10 instead of 0! const name = user.name || 'Anonymous'; // ❌ If name is "" (empty string), this gives 'Anonymous'
Nullish coalescing only falls back for null/undefined:
// ✅ Only falls back if null or undefined const count = apiData?.count ?? 0; // 0 stays 0 const name = user?.name ?? 'Anonymous'; // "" stays "" const items = data?.items ?? []; // safe array default const label = config?.label ?? 'Default Label';
Fix 3: Async Data in React — Guard Before Render
In React, data fetched from an API is not available on the first render. The component renders with initial state (often null or undefined) before the fetch resolves. Always guard your render with a loading or existence check.
Bug — render runs before data loads:
function UserProfile() {
const [user, setUser] = useState(null); // null on first render
useEffect(() => {
fetch('/api/user').then(r => r.json()).then(setUser);
}, []);
// ❌ Crashes on first render — user is null
return <div>{user.name}</div>;
}Fixed — guard with loading state and conditional render:
function UserProfile() {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch('/api/user')
.then(r => r.json())
.then(data => { setUser(data); setLoading(false); });
}, []);
// ✅ Guard patterns
if (loading) return <p>Loading...</p>;
if (!user) return <p>User not found.</p>;
// Safe to access user here
return <div>{user.name}</div>;
}Fix 4: API Response Structure — Always Destructure Safely
One of the most common sources of this error is assuming an API response has a certain shape when it does not. Always validate or inspect the response structure first, then access properties defensively.
Assuming too much about the response:
// API returns: { status: "ok", payload: { users: [...] } }
// But you assumed: { users: [...] }
const res = await fetch('/api/users');
const data = await res.json();
// ❌ TypeError: Cannot read properties of undefined (reading 'map')
data.users.map(u => u.name); // data.users is undefined!Fixed — inspect then destructure with defaults:
const res = await fetch('/api/users');
const data = await res.json();
// ✅ Log to understand the shape first
console.log('API response:', JSON.stringify(data, null, 2));
// ✅ Destructure with safe defaults
const { payload: { users = [] } = {} } = data;
users.map(u => u.name); // safe
// ✅ Or use optional chaining
const users = data?.payload?.users ?? [];Fix 5: Array Methods on Undefined
Calling .map(), .filter(), .find(), or .length on undefined is one of the most common causes of this error in React components rendering lists from API data.
Common crashes with array methods:
const [products, setProducts] = useState(); // ❌ no initial value
// ❌ Cannot read properties of undefined (reading 'map')
return products.map(p => <li key={p.id}>{p.name}</li>);
// ❌ Cannot read properties of undefined (reading 'length')
console.log(products.length);
// ❌ Cannot read properties of undefined (reading 'filter')
const active = products.filter(p => p.active);Fixed — initialize with empty array, guard with ?? []:
// ✅ Initialize state with empty array
const [products, setProducts] = useState([]);
// ✅ Fallback in case API returns undefined
(products ?? []).map(p => <li key={p.id}>{p.name}</li>);
// ✅ Optional chaining with array methods
products?.map(p => <li key={p.id}>{p.name}</li>);
// ✅ Guard before render
{Array.isArray(products) && products.map(p => (
<li key={p.id}>{p.name}</li>
))}Fix 6: Deep Object Access — Lodash get or Optional Chaining Chain
When accessing deeply nested properties (4+ levels), optional chaining chains can get verbose. Lodash _.get() provides a clean alternative with a default value parameter.
Deeply nested access — easy to miss a level:
// ❌ Crashes if any level is undefined
const zip = response.body.data.user.address.zip;
// ❌ Verbose but still error-prone
if (response && response.body && response.body.data &&
response.body.data.user && response.body.data.user.address) {
const zip = response.body.data.user.address.zip;
}Fixed — optional chaining or lodash get:
// ✅ Optional chaining chain const zip = response?.body?.data?.user?.address?.zip ?? 'N/A'; // ✅ Lodash _.get with default import _ from 'lodash'; const zip = _.get(response, 'body.data.user.address.zip', 'N/A'); // ✅ For arrays of objects const firstTag = _.get(post, 'metadata.tags[0].name', 'untagged'); // Equivalent to: post?.metadata?.tags?.[0]?.name ?? 'untagged'
Quick Defensive Programming Patterns
Keep these patterns in your toolkit for writing resilient JavaScript and React code:
// Safe property access
const name = user?.name ?? 'Anonymous';
// Safe method calls
user?.save?.();
// Safe array defaults
const list = data?.items ?? [];
const count = data?.items?.length ?? 0;
// Safe array methods
const names = (users ?? []).map(u => u.name);
const active = users?.filter(u => u.active) ?? [];
// Safe destructuring with defaults
const { name = '', age = 0, roles = [] } = user ?? {};
// Type guards before operations
if (typeof value === 'string') { /* safe to call .trim() */ }
if (Array.isArray(items)) { /* safe to call .map() */ }
if (value != null) { /* safe: not null or undefined */ }
// Early return guard
function getUserCity(user) {
if (!user?.address) return null;
return user.address.city;
}Validate your JSON API response structure
Paste your API response into the JSON Validator to see the exact structure and verify property paths before accessing them in your code.
Open JSON Validator →Frequently Asked Questions
What causes 'Cannot read properties of undefined'?
You tried to access a property on a value that is undefined or null. Common causes: API data not yet loaded, typo in property name, array method called on undefined, or missing key in a nested object.
How does optional chaining fix this?
Optional chaining (?.) returns undefined instead of throwing when the left side is null or undefined. user?.profile?.name returns undefined safely rather than crashing.
How do I safely access deeply nested object properties?
Use optional chaining: user?.address?.city ?? 'Unknown'. For very deep paths, lodash _.get(obj, 'a.b.c', default) never throws regardless of depth.
Why does my API response show undefined in React?
On the first render, async data has not loaded yet. Initialize state with a safe default (null or []) and render conditionally: if (!data) return <Loading />.
What is the difference between null and undefined?
undefined is the absence of assignment; null is an intentional empty value. Both cause this error when you access properties on them. Optional chaining handles both.