React Error #130: Objects are not valid as a React child
Objects are not valid as a React child
Verified against React 19.0.3 source (packages/react/src/ReactCreateElement.js), Stack Overflow #33117449 (top answer, 4.2k upvotes), React docs: rendering-lists · Updated April 2026
> quick_fix
Wrap the object in JSON.stringify() or access a specific property. React can render strings, numbers, and arrays of JSX — but never raw objects.
// ❌ This throws Error #130
<div>{myObject}</div>
// ✅ Fix 1: Stringify
<div>{JSON.stringify(myObject)}</div>
// ✅ Fix 2: Access a property
<div>{myObject.name}</div>What causes this error
React's reconciler can render primitives (string, number, null), valid React elements, and arrays of these. When you drop a plain JavaScript object into JSX, React cannot decide how to represent it as a DOM node and throws invariant #130. This is almost always a mistake — you meant to render a specific field of the object, not the whole thing.
How to fix it
- 01
step 1
Identify which object is being rendered
The error message usually includes the keys of the offending object (e.g., "{name, age, ...}"). Match those keys to variables in your JSX.
- 02
step 2
Decide what you actually want to show
Do you want a specific property? Use dot notation. Do you want the whole object for debugging? Wrap it in JSON.stringify().
// Debug: show all keys <pre>{JSON.stringify(user, null, 2)}</pre> // Production: show a specific field <p>Welcome, {user.name}</p> - 03
step 3
Handle arrays of objects correctly
If you have an array, map over it and render each item as a child element.
users.map(u => <li key={u.id}>{u.name}</li>)
How to verify the fix
- Error #130 is no longer thrown in the console.
- The correct value renders where you expected.
- No warnings about missing `key` prop when rendering lists.
Why #130 happens at the runtime level
React's createElement validates children at render time and walks the element tree to ensure each child is a primitive (string, number), a valid React element, an array of these, or null. When the reconciler encounters a plain object whose constructor is Object (no $$typeof Symbol marking it as a React element), invariant #130 fires from packages/react/src/ReactCreateElement.js. The check is structural, not nominal, Date instances, Map, Set, and plain Promises all fail the same way because none carry the React element brand.
Common debug mistakes for #130
- Wrapping the object in a Fragment (<>{user}</>) and expecting it to render, Fragment children pass through the same child-validation, so the invariant still fires.
- Calling toString() on the object without overriding it, the default Object.prototype.toString returns '[object Object]', which technically renders but is never what was intended.
- Adding a try/catch around the JSX expecting to recover, invariants are thrown synchronously during render and React unmounts the subtree before your handler can run.
- Assuming the error names a wrong file in the trace, the trace points at the component that called createElement, but the bug is usually one render up where the prop was passed.
- Removing the object from JSX without checking if useState held it, if state was set to an object on a previous render, the same error keeps firing until you also fix the setter.
When #130 signals a deeper problem
Recurring #130 across multiple components signals the data layer is leaking unparsed shapes into the view layer. It usually means a fetch wrapper is returning the raw fetch Response or a JSON-parsed envelope object instead of the inner data field, or a GraphQL hook is exposing the entire query result rather than data.users. Fix by adding a typed boundary at the data layer (zod schema, generated TS types, normalised store) so view code only ever receives renderable primitives. Component-level patches keep this error returning every time a new endpoint is wired in.
Editor's take
This error has a particular talent for appearing at the exact moment your staging-to-production promotion is in flight. A small startup running a Next.js 14 App Router frontend against a FastAPI backend will hit this the first time a developer forgets that `response.json()` returns a Promise in some fetch polyfills — or worse, that their custom API client is returning the raw Response object instead of awaited JSON. The result lands in production at 11pm on a Thursday when the platform team lead is the only one on call, and the error boundary swallows it silently until a user reports a blank product card.
Hitting this error in your first month usually means you're still building a mental model of what React's reconciler actually accepts as valid children — which is fine. Fixing it cleanly, by tracing the data shape back through the component tree rather than just slapping a JSON.stringify on the symptom, is the jump to intermediate. What makes it a genuine senior signal is recognizing it as a contract violation between layers: the view received a shape it was never promised. Engineers who've internalized that distinction write better TypeScript interfaces and tend to reach for Zod or Valibot at the API boundary before the bug ever reaches the renderer.
In practice this error rarely travels alone. You'll often find React Warning: Each child in a list should have a unique 'key' prop in the same console session, because the object that broke rendering was itself inside a mapped array. Upstream, check for a TypeError: Cannot read properties of undefined preceding it — the object was likely already malformed before reaching the child. In React Query codebases, look for a stale useQuery selector returning the full QueryObserverResult instead of result.data, which surfaces as Error #130 the moment that selector feeds into JSX.
By Bikram Nath · Curator · Updated April 2026
Frequently asked questions
What does React error #130 mean?
It means you tried to render a JavaScript object directly in JSX. React cannot serialise arbitrary objects to the DOM, so it throws this invariant.
Does this error crash the app?
Yes, in development mode. In production, React unmounts the component tree at the nearest error boundary. Without an error boundary, the entire app unmounts.
Can I catch this error in production?
Yes — wrap suspect components in an ErrorBoundary. But the real fix is to stop passing raw objects to JSX.
Why did this error only appear in production?
Usually because a dev-mode guard (e.g., a type check) was removed in production builds, or because the object shape differs between environments.