This error is a data-shape problem. React renders before async data arrives, so a value that will eventually be an array may be undefined during the first render.
A detailed fix includes both initialization and UI states: initialize list state as [], show loading while the request runs, show an error when the request fails, and confirm the API response is actually an array before calling map.
Cannot read map of undefined in React needs more than a syntax memory trick. The important idea is to understand initial state, async data loading, array guards, optional chaining, loading UI, and API response shape checks in the exact situation where the page topic appears, then prove the behavior with a small working example and one edge case.
The error "Cannot read properties of undefined (reading 'map')" is extremely common in React. It occurs when you try to call .map() on a variable that is undefined or null "" usually because async data hasn't loaded yet, or an API returned an unexpected shape.
// ❌ Problem
const [users, setUsers] = useState(); // undefined!
return users.map(u => <p>{u.name}</p>); // Error!
// ✅ Solution 1: Initialize with empty array
const [users, setUsers] = useState([]);
// ✅ Solution 2: Optional chaining
return users?.map(u => <p>{u.name}</p>);
// ✅ Solution 3: Fallback
return (users || []).map(u => <p>{u.name}</p>);
function ProductList() {
const [products, setProducts] = useState(); // ❌ undefined!
useEffect(() => {
fetch('/api/products')
.then(r => r.json())
.then(setProducts);
}, []);
return products.map(p => <div key={p.id}>{p.name}</div>); // Error on first render!
}
function ProductList() {
const [products, setProducts] = useState([]); // ✅ Empty array default
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch('/api/products')
.then(r => r.json())
.then(data => {
setProducts(data);
setLoading(false);
});
}, []);
if (loading) return <p>Loading...</p>;
return products.map(p => <div key={p.id}>{p.name}</div>);
}
// API returns: { data: { users: [...] } }
useEffect(() => {
fetch('/api/users')
.then(r => r.json())
.then(setUsers); // ❌ Sets users to { data: { users: [...] } }
}, []);
return users.map(u => <p>{u.name}</p>); // Error! users is an object
useEffect(() => {
fetch('/api/users')
.then(r => r.json())
.then(response => {
setUsers(response.data.users); // ✅ Extract the array
});
}, []);
// Or with optional chaining as safety net
return (users ?? []).map(u => <p>{u.name}</p>);
function List({ items }) {
return items.map(item => <li>{item}</li>); // Error if items not passed!
}
// Usage "" forgot to pass items
<List /> // items is undefined!
// ✅ Default prop value
function List({ items = [] }) {
return items.map(item => <li key={item}>{item}</li>);
}
// ✅ Or with PropTypes
import PropTypes from 'prop-types';
List.defaultProps = { items: [] };
List.propTypes = { items: PropTypes.array };
function DataList() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch('/api/data')
.then(r => r.json())
.then(setData)
.catch(setError)
.finally(() => setLoading(false));
}, []);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
if (!data || !Array.isArray(data)) return <p>No data</p>;
return (
<ul>
{data.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
);
}
React does not wait for useEffect before rendering the component. The first render uses the initial state. If the initial state is undefined and JSX calls users.map(...), JavaScript throws before React can show the intended list.
The simplest fix is to initialize the state with the same shape you plan to render. If the UI renders a list, start with an empty array. If the UI renders an object, start with null and guard the object before reading properties.
Many APIs return an object that contains the array, such as { users: [...] } or { data: [...] }. If you store the whole object and then call response.map, the code fails because the object itself is not an array.
Inspect the response once, choose the exact array field, and store that field in state. This makes rendering simpler and prevents repeated optional chaining throughout the JSX.
Cannot read map of undefined usually appears when a React component renders before async data has arrived. The first render happens with the initial state, so if users is undefined and the JSX calls users.map(), JavaScript throws before React can show the list. The fix begins with choosing a safe initial value.
For list data, initialize state as an empty array when the UI expects an array. If the API shape is uncertain, verify it before storing it. Loading and empty states also make the component clearer: loading explains that data is still coming, empty explains that the request succeeded but there are no items.
function UserList() {
const [users, setUsers] = useState();
return users.map(user => <p key={user.id}>{user.name}</p>);
}
function UserList() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch("/api/users")
.then(res => res.json())
.then(data => setUsers(Array.isArray(data) ? data : data.users ?? []))
.finally(() => setLoading(false));
}, []);
if (loading) return <p>Loading...</p>;
return users.map(user => <p key={user.id}>{user.name}</p>);
}
function UsersList() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch('/api/users')
.then(res => res.json())
.then(data => setUsers(Array.isArray(data) ? data : []))
.finally(() => setLoading(false));
}, []);
if (loading) return <p>Loading...</p>;
return users.map(user => <p key={user.id}>{user.name}</p>);
}
Starting list state as undefined.
Start with [] when JSX maps the value.
Mapping the whole API response object.
Map the exact array field.
Using optional chaining as the only fix.
Also handle loading, errors, and wrong data shape.
Adding optional chaining everywhere without checking why the data is undefined.
Set a correct initial state and validate the API response before rendering the list.
Because data fetched in useEffect isn't available on the first render. The component renders once with the initial state (undefined if not set), then useEffect runs and fetches data, triggering a second render with the actual data.
Initialize your state as an empty array: useState([]). Add a loading state to show a spinner while data loads. Use optional chaining: data?.map() as a safety net.
Both cause the same error. undefined is the default when no value is assigned. null is explicitly set to "no value". Both need to be handled before calling .map().
Use a try/catch block or .catch() in your fetch chain. Store the error in state and show an error message. Always check if data exists and is an array before mapping.
Yes! data?.map(item => ...) returns undefined if data is null/undefined instead of throwing an error. You can also use (data ?? []).map() to fall back to an empty array.
Explore 500+ free tutorials across 20+ languages and frameworks.