How to Fix a Blank React Dashboard (Failed Fetch)
TypeError: users.map is not a function on a blank React dashboard almost always means your fetch function returned the wrapper object instead of the array inside it. The API replied with {data: [...]} - your code returned the whole thing and React tried to call .map() on an object.
What causes users.map is not a function
Many APIs wrap their array responses in an envelope object:
{
"data": [
{"id": 1, "name": "Alice"},
{"id": 2, "name": "Bob"}
]
}
If your fetch function returns the whole response object, the component receives
{data: [...]} - not an array. Calling .map() on a plain object throws:
TypeError: users.map is not a function
React renders nothing, and the dashboard goes blank.
How to spot it
Open the browser Network tab, click the /api/users request, and read the
Response body. If you see a {data: [...]} wrapper - not a bare [...] array -
the shape mismatch is the bug. You can also test the fetch function directly from
the terminal:
node -e "console.log(require('./loadUsers.js').loadUsers({data: [1,2,3]}))"
If this prints the whole object ({ data: [ 1, 2, 3 ] }) instead of the array
([ 1, 2, 3 ]), you've confirmed the issue.
The fix: return response.data
Drill into the envelope and return the array:
// Before - returns the wrapper object
async function loadUsers() {
const response = await fetch("/api/users").then(r => r.json());
return response;
}
// After - returns the array inside it
async function loadUsers() {
const response = await fetch("/api/users").then(r => r.json());
return response.data;
}
The component can now call users.map(...) without crashing.
Why this pattern is so common
API designers wrap arrays in objects for good reasons - to add pagination metadata, status flags, or error fields alongside the data without breaking the shape. A response like:
{"data": [...], "total": 248, "page": 1}
...is harder to extend than a bare array, because adding a field later doesn't change the fact that the outer value is an array. Envelope patterns are everywhere (Axios, Django REST Framework, JSON:API, GitHub API), so learning to read the actual response shape rather than trusting the variable name is a 30-second debugging habit that saves hours of incident time.
Preventing it going forward
- Check the raw response first - Network tab or
console.log(response)before wiring into state. Trust what the API actually returns, not what you expect. - Use TypeScript - typed API clients (openapi-typescript-codegen, tRPC, or
hand-written types) make the compiler reject
responsewhereresponse.datais expected, catching shape mismatches at build time instead of in production. - Guard the state - initialize with an empty array (
const [users, setUsers] = useState([])) so the first render doesn't crash even if fetch is slow.
Want to try it hands-on? HeyDevJob gives you this exact setup in a live cloud workspace in your browser - edit it, run it, and see it work. Free, nothing to install.
Try it in a workspace →What you'll practice
- Reading the Network tab to compare the actual API response shape against what the component receives
- Returning response.data instead of response when the API uses an envelope wrapper
- Using console.log or the Node REPL to test a fetch utility function before wiring it into React state
FAQ
Why does my React component get 'users.map is not a function'?
The component is receiving an object instead of an array - usually because the fetch function returned the full API response ({data: [...]}) instead of the array inside it (response.data). Check the Network tab to see the actual response shape.
How do I fix a blank React dashboard caused by a fetch error?
Open the browser console and Network tab. If .map() is throwing 'is not a function', your state variable holds an object, not an array. Trace back to the fetch function and confirm it returns the correct nested field - often response.data instead of response.
What does it mean when an API returns {data: [...]} instead of an array?
The API is using an envelope pattern - wrapping the array in an object to allow extra fields like pagination or status. You need to access .data (or whatever the key is) in your fetch function to get the array your component needs.
Keep learning
Learn it by doing. Open this in a live cloud workspace, make the change yourself, and keep a record of the work you can share.
Open the workspace →