Quick Answer
If you're in a hurry, change yourArray.map(...) to (yourArray || []).map(...) or use optional chaining: yourArray?.map(...). That stops the crash, but read on for the real fix.
Why This Happens
This error shows up when a variable you're trying to call .map() on is undefined at the moment React renders the component. The classic scenario? You fetch data from an API inside useEffect(), set it with useState(), and try to render it immediately. On the first render, that state is undefined because the fetch hasn't completed yet, so undefined.map() throws the error.
Another common trigger is when you pass data as props from a parent component that also loads asynchronously, or when you're using a library like React Router and the route params aren't ready on the first render.
Fix Steps (The Real Fix)
- Initialize your state with an empty array. The cleanest solution is to set a default value in
useState():
This way,const [items, setItems] = useState([]);itemsis always an array, and.map()works even before data arrives. If you're using TypeScript, type it asuseState.- ([])
- Check where the data comes from. If the state is set correctly, the problem might be in how you access the array. For example, if your API returns
{ data: [...] }, you might be doingresponse.data.map()instead ofresponse.map(). Log your API response to see exactly what you're getting. - Guard against undefined in render with conditional rendering. If you can't change the state initialization (maybe it's a prop), wrap your map in a check:
{items && items.length > 0 ? ( items.map(item =>{item.name}) ) : (Loading...
)} - Use optional chaining. This is a modern JavaScript feature that returns
undefinedinstead of throwing when the property doesn't exist:
This is a quick patch, not a solution. It hides the problem, so use it only as a temporary stopgap.items?.map(item => ...)
Alternative Fixes
Sometimes the main fix doesn't cover it, especially if the data comes from a complex source. Here are a few other things to try:
- Set a fallback in the map call itself:
(items || []).map(...). This is basically the same as optional chaining but works in older browsers and Node versions. - Use a loading state. Add a
loadingboolean to your state, set it totruebefore fetching, thenfalseafter. In render, checkif (loading) return <p>Loading…</p>before the map. This is the most robust approach for async data. - Check the shape of your props. If you're receiving data from a parent, make sure the parent isn't passing
undefinedon the first render. You can set a default value in the child component's props:function MyList({ items = [] }). - Use a custom hook that handles the undefined case. Create a hook like
useFetchthat returns{ data, loading, error }and always returns an empty array for data when there's nothing yet.
I've seen people try to use try/catch around .map(), but that doesn't work because the error happens during render, and catch blocks only catch errors in synchronous code outside of JSX. Skip that.
Prevention Tips
Stop these errors before they start with these habits:
- Always initialize state with the right type. If it's an array, use
[]. If it's an object, use{}ornulland handle that case. - Use TypeScript. It won't catch this at compile time unless you're strict, but it forces you to type your state and props, making it obvious when something might be undefined.
- Write a helper to safely map. Like this:
const safeMap = (arr, fn) => (Array.isArray(arr) ? arr.map(fn) : []);Then usesafeMap(items, ...)everywhere. It's overkill for small projects, but if you're dealing with lots of async data, it saves you from dozens of these crashes. - Test your rendering logic. Use React Testing Library and simulate the initial render before data arrives. That catches this error in tests, not in production.
The root cause is almost always a race condition between fetching and rendering. Fix the state initialization and you'll kill 90% of these errors. The rest are just edge cases you'll handle as you go.