How to fix Cannot read properties of undefined (reading 'map') in React
This error occurs when you try to call the .map() method on a variable that is undefined, typically when rendering lists in React components. The issue happens because data hasn't loaded yet, a prop wasn't passed, or an API response is being used before it's fetched.
Quick fix
Fixes Cannot read properties of undefined (reading 'map')
function UsersList({ users }) {
return (
<div>
{users.map(user => <p key={user.id}>{user.name}</p>)} // ❌ Error if users is undefined
</div>
);
}- Add a null/undefined check before calling .map()
- Use a default value for array props
- Initialize state with an empty array
- Add a loading state and conditional rendering
function UsersList({ users }) {return (<div>{users.map(user => <p key={user.id}>{user.name}</p>)} // ❌ Error if users is undefined</div>);}Cannot read properties of undefined (reading 'map')
What this error means
This error occurs when JavaScript attempts to access the "map" property on a value that is undefined. In React, this almost always happens when rendering arrays conditionally—you're trying to map over data that hasn't been initialized, hasn't loaded yet, or wasn't provided. The error is fundamental: undefined is not an array and has no methods. When React tries to render something like `data.map(item => <div>{item}</div>)` and `data` is undefined, JavaScript throws this TypeError immediately. This is one of the most common React errors because data loading is asynchronous, and components often render before data arrives. The issue is particularly common when: - Fetching data from an API with useEffect - Receiving props that might be undefined - Destructuring objects that don't exist yet - Accessing nested properties on undefined objects - Using state that hasn't been initialized The fix always involves ensuring the data exists and is an array before calling .map() on it.
Typical symptoms
- Error message shows "Cannot read properties of undefined (reading 'map')" in browser console
- White screen of death or blank page when component renders
- Error appears only on first load, then disappears after data loads
- Component crashes when trying to render a list
- Browser shows unhandled promise rejection if data fetch fails
- Error occurs specifically at the line where .map() is called
- Works fine in development but fails intermittently in production
Common causes
- Calling .map() on undefined data before it loads from an API
- Props not being passed to a component that expects an array
- Destructuring an undefined prop or object property
- Accessing nested properties without checking if parent exists
- State variable not initialized with a default value
- Async data fetch hasn't completed when component renders
- API response returns null or undefined instead of an array
- Forgetting to check array existence before mapping over it
How to fix "Cannot read properties of undefined (reading 'map')"
The simplest fix is to check if the data exists before mapping:
Bad - No check:
function UsersList({ users }) {
return (
<div>
{users.map(user => <p key={user.id}>{user.name}</p>)} // ❌ Error if users is undefined
</div>
);
}Good - Check before mapping:
function UsersList({ users }) {
return (
<div>
{users && users.map(user => <p key={user.id}>{user.name}</p>)}
</div>
);
}Or with optional chaining (ES2020):
function UsersList({ users }) {
return (
<div>
{users?.map(user => <p key={user.id}>{user.name}</p>)}
</div>
);
}Set a default value in function parameters or with PropTypes to ensure an array is always provided:
Using default parameters:
function UsersList({ users = [] }) {
return (
<div>
{users.map(user => <p key={user.id}>{user.name}</p>)}
</div>
);
}Using PropTypes:
import PropTypes from 'prop-types';
function UsersList({ users }) {
return (
<div>
{users.map(user => <p key={user.id}>{user.name}</p>)}
</div>
);
}
UsersList.propTypes = {
users: PropTypes.array.isRequired // Enforces array type
};
UsersList.defaultProps = {
users: [] // Default to empty array
};With TypeScript:
interface UsersListProps {
users: User[]; // Always an array
}
function UsersList({ users = [] }: UsersListProps) {
return (
<div>
{users.map(user => <p key={user.id}>{user.name}</p>)}
</div>
);
}When using useState for data, always initialize with an empty array instead of undefined:
Bad - Initializes to undefined:
function UsersList() {
const [users, setUsers] = useState(); // undefined initially
return (
<div>
{users.map(user => <p key={user.id}>{user.name}</p>)} // ❌ Error
</div>
);
}Good - Initializes to empty array:
function UsersList() {
const [users, setUsers] = useState([]); // Empty array initially
return (
<div>
{users.map(user => <p key={user.id}>{user.name}</p>)} // ✓ Safe
</div>
);
}When fetching data asynchronously, use a loading state to handle the time before data arrives:
Example with useEffect:
function UsersList() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchUsers() {
try {
const response = await fetch('/api/users');
const data = await response.json();
setUsers(data); // Set array safely
setLoading(false);
} catch (err) {
setError(err.message);
setLoading(false);
}
}
fetchUsers();
}, []);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error}</p>;
return (
<div>
{users.map(user => <p key={user.id}>{user.name}</p>)}
</div>
);
}With React Query (modern approach):
import { useQuery } from '@tanstack/react-query';
function UsersList() {
const { data = [], isLoading, error } = useQuery({
queryKey: ['users'],
queryFn: async () => {
const response = await fetch('/api/users');
return response.json();
}
});
if (isLoading) return <p>Loading...</p>;
if (error) return <p>Error loading users</p>;
return (
<div>
{data.map(user => <p key={user.id}>{user.name}</p>)}
</div>
);
}When accessing nested array properties, ensure all parent objects exist:
Bad - No nested checks:
function PostList({ response }) {
return (
<div>
{response.data.posts.map(post => <p key={post.id}>{post.title}</p>)} // ❌ Error if response, data, or posts is undefined
</div>
);
}Good - With optional chaining:
function PostList({ response }) {
return (
<div>
{response?.data?.posts?.map(post => <p key={post.id}>{post.title}</p>)}
</div>
);
}Or with explicit checks:
function PostList({ response }) {
if (!response || !response.data || !response.data.posts) {
return <p>No posts available</p>;
}
return (
<div>
{response.data.posts.map(post => <p key={post.id}>{post.title}</p>)}
</div>
);
}Check your API documentation and verify the response structure before using it:
function UsersList() {
const [users, setUsers] = useState([]);
useEffect(() => {
fetch('/api/users')
.then(response => response.json())
.then(data => {
// Log the response to verify structure
console.log('API response:', data);
// Check if data is array or nested inside response object
if (Array.isArray(data)) {
setUsers(data);
} else if (Array.isArray(data.users)) {
setUsers(data.users); // Some APIs nest arrays
} else if (data && data.results) {
setUsers(data.results);
} else {
console.error('Unexpected API response structure');
setUsers([]); // Fallback to empty array
}
})
.catch(err => {
console.error('Fetch error:', err);
setUsers([]); // Fallback on error
});
}, []);
return (
<div>
{users.map(user => <p key={user.id}>{user.name}</p>)}
</div>
);
}Always verify with console.log() or browser DevTools to confirm the actual API response before assuming its structure.
TypeScript can catch these errors at compile time instead of runtime:
interface User {
id: number;
name: string;
}
interface UsersListProps {
users: User[]; // Explicitly types as array
}
function UsersList({ users }: UsersListProps) {
// TypeScript guarantees users is User[] array, never undefined
return (
<div>
{users.map(user => (
<p key={user.id}>{user.name}</p>
))}
</div>
);
}
// Or with async data:
function UserListContainer() {
const [users, setUsers] = useState<User[]>([]); // Explicit type
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchUsers()
.then(data => {
setUsers(data); // TypeScript checks data is User[]
setLoading(false);
})
.catch(() => setLoading(false));
}, []);
if (loading) return <p>Loading...</p>;
return <UsersList users={users} />; // Type-safe
}TypeScript will prevent you from passing undefined, null, or wrong types to .map().
Advanced notes
Array vs Array-like Objects: The .map() method only exists on true JavaScript arrays. Array-like objects (NodeList, arguments object) won't have .map(). Always convert them to arrays first: Array.from(nodeList).map(...) or [...nodeList].map(...).
Falsy vs Undefined: In React, checking if (data) handles both undefined and null, but also false, 0, and empty strings. Be careful when the value might legitimately be 0 or false. Use explicit checks: if (data !== undefined && data !== null) or with optional chaining: data?.map(...).
Performance with Empty Arrays: Initializing state with an empty array has minimal performance impact. React will render once with the empty array, then again when data loads. If this causes flickering, use the loading state approach (Step 4) to show a loading indicator instead.
React Suspense (Experimental): In React 18+, you can use Suspense for cleaner async data handling, but this is still experimental. It requires a compatible data fetching library like Relay or using frameworks like Next.js.
Error Boundaries: If you want to handle this error globally, wrap components with an Error Boundary:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, info) {
console.log('Error caught:', error, info);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong</h1>;
}
return this.props.children;
}
}Custom Hooks for Data Fetching: Extract the loading/error/data logic into a custom hook to avoid repetition across components:
function useFetch<T>(url: string): { data: T[]; loading: boolean; error: Error | null } {
const [data, setData] = useState<T[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
fetch(url)
.then(r => r.json())
.then(setData)
.catch(setError)
.finally(() => setLoading(false));
}, [url]);
return { data, loading, error };
}Debugging Tip: If unsure about data shape, always log the response: console.log('data:', data, 'is array:', Array.isArray(data)). This immediately shows whether the problem is undefined, null, or wrong structure.
Frequently asked questions
- What does "Cannot read properties of undefined (reading 'map')" mean?
- This error occurs when JavaScript attempts to access the "map" property on a value that is undefined. In React, this almost always happens when rendering arrays conditionally—you're trying to map over data that hasn't been initialized, hasn't loaded yet, or wasn't provided. The error is fundamental: undefined is not an array and has no methods.
- How do I fix "Cannot read properties of undefined (reading 'map')"?
- This error occurs when you try to call the .map() method on a variable that is undefined, typically when rendering lists in React components. The issue happens because data hasn't loaded yet, a prop wasn't passed, or an API response is being used before it's fetched.
- What causes "Cannot read properties of undefined (reading 'map')"?
- Calling .map() on undefined data before it loads from an API Props not being passed to a component that expects an array Destructuring an undefined prop or object property
- Is the "Cannot read properties of undefined (reading 'map')" error serious?
- Yes. This is a high-severity error that usually blocks work until it is fixed, but the steps above resolve it.
Official resources & further reading
- 📚React Official Documentation
- 📖React Docs: Rendering Lists(Official Docs)
- 📖React Docs: useState Hook(Official Docs)
- 📖React Docs: useEffect Hook(Official Docs)
- 📖MDN: Array.prototype.map()(Official Docs)