Skip to content
Code2026-09-074 min read

How to Fix 'Cannot read properties of undefined (reading map)'

Symptom: white screen, one red line in the console

TypeError: Cannot read properties of undefined (reading 'map')

Nine times out of ten it points at your list rendering line:

{users.map((u) => <UserCard key={u.id} user={u} />)}

The page goes blank (React 18 unmounts the whole tree on uncaught render errors) and the console shows a component stack. The error is not about .map — it says users is undefined, and calling anything on undefined throws.

Root causes: the three usual suspects

| Cause | Typical scenario | Signature | |---|---|---| | Async data not arrived | Rendering data.list.map(...) before fetch resolves | Crashes on first render; data arriving later doesn't recover the white screen | | Field name mismatch | API returns { code, data: [...] } but code reads res.list | Crashes forever; the real structure is visible in the Network panel | | Parent object missing | user.address is undefined, so user.address.cities.map(...) throws | Data with optional/foreign-key related fields where some records lack them |

Step one: look at what the API actually returns

Don't guess. Copy the raw response body and paste it into the JSON Formatter — the hierarchy expands instantly and you can see exactly which field holds the list (data? data.list? a top-level array?). To test the endpoint itself (including CORS-blocked or plain-http targets), fire it from the API Tester — server-proxy mode by default, immune to browser CORS limits.

The most common surprise: the API returns { list: [...] } on success but { message: "..." } on error — list simply disappears. Code written only for the happy path explodes the moment an error response arrives.

Fixes, in order of preference

1. A safe initial value (default empty array)

const [users, setUsers] = useState([]); // not useState() or null

While data is in flight, users.map runs against an empty array and renders nothing — no crash, no white screen. The single cheapest, highest-value line you can change.

2. Optional chaining + empty-array default (uncertain paths)

{(data?.list ?? []).map(...)}
// or for nested fields
{(user?.address?.cities ?? []).map(...)}

?. short-circuits on undefined; ?? [] guarantees .map always has an array. Note: this is a guard, not a band-aid — if the field never exists, the real fix is correcting the field name, not stacking question marks.

3. Conditional rendering (when loading vs empty matter)

{loading ? <Skeleton /> : users.length === 0 ? <Empty /> : users.map(...)}

Best UX, most code. Worth it for list pages; overkill for small components.

4. ErrorBoundary as the last line of defense

In React 18 one crashing component blanks the page. Wrap fragile regions in an ErrorBoundary so a crash degrades to a fallback UI instead of killing everything. Frameworks ship one (Next.js error.tsx); roll-your-own setups should add it.

FAQ

Works locally, crashes in test — why?

Local mocks always return the full shape; the test environment's API returns an error shape on failure — the missing field triggers the crash. Make your mock deliberately return one error-shaped response and this class of bug surfaces during development.

Added ?. and the list is just empty?

The data truly isn't there. Back to the Network panel: did the request fire? What status? Does the body contain the field you read? Three questions, all answered in one place.

Why does TypeScript not catch this?

users: User[] only checks assignments — runtime API responses aren't validated. For real defense use type guards, or generate types from a real response with JSON to TypeScript — the generated type honestly reflects optionality like { list?: User[] }.

Is reading 'length' or reading '0' the same bug?

Yes. The text after reading is the property you accessed on undefined — the fix is always "find out why the left side is undefined, then guard or fix the field".

The checklist (bookmark this)

  1. Open the component stack, locate the exact .map line
  2. Inspect the raw response (or paste into the JSON Formatter)
  3. Confirm the real list path matches the code
  4. Guard with useState([]) / ?? [] / conditional rendering
  5. Add an ErrorBoundary on list pages so crashes don't spread

Provided by ToolVault. Related tools: JSON Formatter, API Tester, JSON to TypeScript. See the homepage for more developer tools.


Advertisement