'Objects are not valid as a React child': what it means and 6 ways it happens
React can render strings and numbers - not objects. This error fires the moment JSX receives one, and the culprit is usually a Date, a Promise, or a whole object where you meant a field.
'Objects are not valid as a React child (found: object with keys {...})' is React telling you that something inside your JSX curly braces evaluated to a plain object. JSX can render strings, numbers, elements, and arrays of those - never raw objects. The error message even lists the object's keys, which is your best clue to which value leaked in.
The six usual suspects
// 1. โ Rendering the whole object instead of a field
<p>{user}</p> // โ
<p>{user.name}</p>
// 2. โ Date objects aren't strings
<p>{createdAt}</p> // โ
<p>{createdAt.toLocaleDateString()}</p>
// 3. โ Forgot to await / rendered a Promise
<p>{fetchUser(id)}</p> // fetch in an effect or use a query library
// 4. โ Called a component like a function that returns... itself
{MyComponent} // โ
<MyComponent />
// 5. โ Array of objects without mapping to elements
{users} // โ
{users.map(u => <li key={u.id}>{u.name}</li>)}
// 6. โ Style/i18n object passed as a child by accident
<p>{styles}</p>Number 1 and number 2 account for most Stack Overflow duplicates. An API returns { name, email }, you write {user} instead of {user.name}, and the error names the keys: found: object with keys {name, email}. Match those keys against your data shapes and you've found the line.
Why 'found: object with keys {seconds, nanoseconds}' means Firestore
Certain key lists are fingerprints. {seconds, nanoseconds} is a Firestore Timestamp - call .toDate() first. {_isAMomentObject} is a Moment instance. {$$typeof} usually means you rendered a component reference without JSX brackets. Learning to read the keys turns this from a mystery into a 30-second fix.
Prevent it with TypeScript
// ReactNode excludes plain objects, so this fails at compile time:
const cell: React.ReactNode = user; // โ Type error - caught before runtime
// A tiny helper for debug rendering when you DO want the object:
<pre>{JSON.stringify(user, null, 2)}</pre>In a strict TypeScript codebase this error is almost extinct, because passing an object where ReactNode is expected fails the build. If you're on plain JavaScript, the JSON.stringify pattern is the honest workaround while debugging - just don't ship it.
Read the keys in the error message. They tell you exactly which object leaked into your JSX - the rest is a project-wide search.
Written by Appesto Engineering.