Objects are not valid as a React child
React was asked to show a whole object, such as a database row or a date, on the page, and it can only show text, numbers, and elements.
Also appears as
- Objects are not valid as a React child (found: [object Date]). If you meant to render a collection of children, use an array instead.
- Minified React error #31; visit https://react.dev/errors/31?args[]=object%20with%20keys%20%7Bid%2C%20title%7D for the full message or use the non-minified dev environment for full errors and additional helpful warnings.
Prompt to fix it
My React app crashes with "Objects are not valid as a React child (found: object with keys {...})". Use the keys in the message to find which object is being rendered and where. Change the JSX to render the specific field it needs, such as todo.title or error.message, map arrays of objects to elements with stable keys, and convert dates to strings before rendering. If the data shape changed, update the TypeScript types to match. Don’t paper over it by rendering JSON.stringify in the UI.
Paste into Layerwise or any AI coding tool.
What it means
Between JSX tags, React can render strings, numbers, elements, and arrays of those. A plain object has no obvious way to appear on screen, so React stops and lists the object’s keys in the error. Those keys tell you which value it was: {id, title} is a row from your data.
This often starts when data changes shape. Code that rendered {post.author} breaks once author becomes a joined record with its own fields, and code that renders {todo} where it meant {todo.title} never worked. Dates and error objects count too, so render {error.message} and a formatted date string.
Double curly braces are a quieter version of the same bug. Template languages like Vue and Handlebars use {{ title }}, but in JSX the outer braces start JavaScript and the inner ones create an object, so React receives { title }.
Common causes
- The JSX renders a whole row or API object, such as
{todo}, where it meant one field like{todo.title}. - A field changed from text to an object, for example a Supabase join that returns
{ id, name }forauthor. - A
Dateor an error object goes straight into the markup. - The JSX uses double braces, as in
<p>{{ title }}</p>, which builds an object.
How to fix it
- Read the keys in the error to identify the object, then find where the component renders it.
- Render one field of the object:
{todo.title},{post.author.name}, or{error.message}. - Turn dates into text before rendering them, for example with
toLocaleDateString(). - For a list, map each object to an element:
{todos.map((todo) => <li key={todo.id}>{todo.title}</li>)}. - To look inside an object while debugging, render
<pre>{JSON.stringify(data, null, 2)}</pre>, and remove it when you’re done.
Example fix
type Todo = { id: string; title: string };
export function TodoList({ todos }: { todos: Todo[] }) {
// Breaks: <ul>{todos}</ul> renders each todo object as a child.
return (
<ul>
{todos.map((todo) => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
);
}Related errors
All errorsEach child in a list should have a unique "key" prop
A list rendered with map gave its items no stable ID, so React can’t tell which item is which when the list changes.
Cannot read properties of undefined
The code asked for a property of a value that doesn’t exist yet, usually data that hasn’t loaded or a field the API didn’t return.
X is not a function
The code called something that isn’t a function, most often .map on API data that came back as an object or a string instead of an array.
Hydration failed
The HTML the server sent didn’t match what React rendered first in the browser, so React threw that part away and rebuilt it on the client.
Build with an agent that checks its work
The Layerwise agent clicks through the live preview and reads the console, so it catches errors before you do.