This error occurs when you try to render a JavaScript object, array, or Promise directly in JSX. React can only render primitive values (strings, numbers) and React elements, not plain objects.
React's rendering engine expects to receive valid React children—primitive types like strings and numbers, React elements created with JSX, or arrays of these valid types. When you attempt to render a plain JavaScript object directly in JSX, React doesn't know how to convert it into DOM elements and throws this error. The error message often includes details about what type of object was encountered, such as "object with keys {}" for plain objects, "[object Promise]" for async functions, or "[object Date]" for Date instances. This restriction exists because React needs to serialize your components into a virtual DOM tree and eventually into real DOM nodes. Objects, Promises, and other complex JavaScript types don't have a default string representation that makes sense to display in the UI. The error is React's way of preventing unpredictable rendering behavior and helping developers catch mistakes early. The issue commonly occurs when developers forget to extract specific properties from objects, accidentally render entire objects returned from API calls, use double curly braces in JSX, render arrays without mapping over them, or call async functions directly in JSX without resolving the Promise first.
React's error message includes a component stack trace showing where the error occurred. Look for the specific component and line number mentioned in the error:
Error: Objects are not valid as a React child (found: object with keys {name, age}).
at MyComponent (MyComponent.jsx:15)
at App (App.jsx:8)Use your browser's developer tools to examine the full error stack. The error will point to the exact JSX expression causing the problem. Look for curly braces {} in your JSX where you might be rendering an entire object.
If you're trying to display data from an object, render specific properties individually:
// Before (causes error)
function UserCard({ user }) {
return <div>{user}</div>; // Error: rendering entire object
}
// After (correct)
function UserCard({ user }) {
return (
<div>
<h2>{user.name}</h2>
<p>Age: {user.age}</p>
<p>Email: {user.email}</p>
</div>
);
}
// Or for debugging, use JSON.stringify
function DebugCard({ data }) {
return <pre>{JSON.stringify(data, null, 2)}</pre>;
}Always extract and render specific properties that contain primitive values (strings, numbers, booleans).
When rendering arrays, you must iterate over them and transform each item into valid React elements:
// Before (causes error)
function UserList({ users }) {
return <div>{users}</div>; // Error: rendering array directly
}
// After (correct)
function UserList({ users }) {
return (
<div>
{users.map((user) => (
<div key={user.id}>
<h3>{user.name}</h3>
<p>{user.email}</p>
</div>
))}
</div>
);
}
// With separate component for clarity
function UserList({ users }) {
return (
<ul>
{users.map(user => (
<UserListItem key={user.id} user={user} />
))}
</ul>
);
}Always include a unique key prop when mapping over arrays to help React efficiently update the DOM.
Using double curly braces creates an object literal instead of referencing a variable:
// Before (causes error)
function Greeting({ message }) {
return <div>{{message}}</div>; // Creates {message: "value"}
}
// After (correct)
function Greeting({ message }) {
return <div>{message}</div>; // Renders the string value
}
// Double braces are only needed for inline styles
function StyledDiv() {
return <div style={{ color: "red", fontSize: 16 }}>Styled</div>;
}Use single curly braces for variables and expressions, double curly braces only for object literals like inline styles.
If you're calling async functions, resolve the Promise before rendering the data:
// Before (causes error)
function AsyncComponent() {
const data = fetchData(); // Returns a Promise
return <div>{data}</div>; // Error: rendering Promise object
}
// After (correct)
import { useEffect, useState } from 'react';
function AsyncComponent() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function getData() {
try {
const result = await fetchData();
setData(result);
} catch (error) {
console.error(error);
} finally {
setLoading(false);
}
}
getData();
}, []);
if (loading) return <div>Loading...</div>;
if (!data) return <div>No data</div>;
return <div>{data.value}</div>;
}For Next.js 13+ App Router, you can use async Server Components, but Client Components still need useEffect.
Date objects must be formatted before rendering:
// Before (causes error)
function EventCard({ event }) {
return (
<div>
<h2>{event.title}</h2>
<p>Date: {event.date}</p> {/* Error if date is a Date object */}
</div>
);
}
// After (correct)
function EventCard({ event }) {
return (
<div>
<h2>{event.title}</h2>
<p>Date: {event.date.toLocaleDateString()}</p>
{/* Or: {event.date.toISOString()} */}
{/* Or: {new Date(event.date).toDateString()} */}
</div>
);
}
// Using modern formatting
function EventCard({ event }) {
const formattedDate = new Intl.DateTimeFormat('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
}).format(event.date);
return (
<div>
<h2>{event.title}</h2>
<p>Date: {formattedDate}</p>
</div>
);
}Use Date methods like toLocaleDateString(), toISOString(), or Intl.DateTimeFormat for proper formatting.
TypeScript can catch many of these errors before runtime:
interface User {
id: number;
name: string;
email: string;
}
interface UserCardProps {
user: User;
}
// TypeScript will warn if you try to render the entire user object
function UserCard({ user }: UserCardProps) {
// return <div>{user}</div>; // Type error: JSX element type 'User' is not constructible
// Correct: render specific properties
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
}
// Define explicit return types
function UserList({ users }: { users: User[] }): JSX.Element {
return (
<div>
{users.map(user => (
<UserCard key={user.id} user={user} />
))}
</div>
);
}TypeScript's type checking helps prevent accidentally rendering objects by enforcing valid children types at compile time.
For debugging complex nested objects, create a custom DebugView component that recursively renders object structures with proper formatting. When working with deeply nested API responses, consider using libraries like lodash to safely access nested properties (e.g., _.get(obj, "user.profile.name", "default")) to avoid errors when intermediate properties are undefined. In production environments, implement error boundaries to catch these errors gracefully and display fallback UI instead of crashing the entire application. For Next.js projects using Server Components (app directory), remember that async components are supported on the server but not in Client Components—use the "use client" directive and useEffect pattern for client-side async data fetching. When working with React Query, Suspense, or other data-fetching libraries, ensure you handle loading and error states properly to avoid rendering Promise objects. Consider using ESLint plugins like eslint-plugin-react to catch potential object rendering issues during development.
Prop spreading could cause security issues
Prop spreading could cause security issues
Error: error:0308010C:digital envelope routines::unsupported
Error: error:0308010C:digital envelope routines::unsupported
React Hook "useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render.
React Hook useEffect placed inside a condition
Hook can only be called inside the body of a function component
Hook can only be called inside the body of a function component
Rollup failed to resolve import during build
How to fix "Rollup failed to resolve import" in React