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.

Browser console
Objects are not valid as a React child (found: object with keys {id, title}). If you meant to render a collection of children, use an array instead.

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 } for author.
  • A Date or 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

  1. Read the keys in the error to identify the object, then find where the component renders it.
  2. Render one field of the object: {todo.title}, {post.author.name}, or {error.message}.
  3. Turn dates into text before rendering them, for example with toLocaleDateString().
  4. For a list, map each object to an element: {todos.map((todo) => <li key={todo.id}>{todo.title}</li>)}.
  5. 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>
  );
}
All errors
Each child in a list should have a unique "key" prop. Check the render method of `TodoList`. See https://react.dev/link/warning-keys for more information.

Each 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.

TypeError: Cannot read properties of undefined (reading 'map')

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.

TypeError: data.map is not a function

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 because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used: - A server/client branch `if (typeof window !== 'undefined')`. - Variable input such as `Date.now()` or `Math.random()` which changes each time it's called. - Date formatting in a user's locale which doesn't match the server. - External changing data without sending a snapshot of it along with the HTML. - Invalid HTML tag nesting. It can also happen if the client has a browser extension installed which messes with the HTML before React loaded. https://react.dev/link/hydration-mismatch

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.