Learn why passing plain objects directly into JSX crashes your React app and how to fix it cleanly.
At‑a‑Glance Fix
Convert any plain object into a renderable value before placing it in JSX, typically by:
- Accessing a specific primitive (e.g., user.name).
- Serialising the whole object (e.g., JSON.stringify(user)).
- Mapping an array of objects to an array of React elements.
Why This Error Happens
React’s renderer accepts strings, numbers, React nodes, and arrays/fragments of nodes. A bare object has no visual representation, so React throws:
Error: Objects are not valid as a React child (found: object with keys {...})
This usually happens when data from an API, state, or props is passed straight into JSX without being broken down first.
Prerequisites
- React 16+ (hooks available)
- Working knowledge of JS objects, arrays, and data fetching
- Optional: TypeScript for stronger type safety
Step‑by‑Step Guide
Follow these hands-on steps to identify, fix, and prevent this common rendering mistake in React.
Step 1: Spot the Offending Code
function UserProfile({ user }: { user: object }) {
return (
<div>
{/* ❌ Passing entire object – will crash */}
{user}
</div>
);
}
Step 2: Render Primitive Fields or Serialise
function UserProfile({ user }: { user: User }) {
return (
<div>
{/* ✅ Render individual fields */}
<p>Name: {user.name}</p>
<p>Email: {user.email}</p>
{/* ✅ Or serialise for debugging */}
<pre>{JSON.stringify(user, null, 2)}</pre>
</div>
);}
Step 3: Handle Arrays the React Way
type User = { id: number; name: string };
const UserList: React.FC<{ users: User[] }> = ({ users }) => (
<ul>
{users.map(({ id, name }) => (
<li key={id}>{name}</li> // ✅ render primitive
))}
</ul>
);
Step 4: Full Working Demo
import React from "react";
// Mock data
action const demoUser = {
id: 1,
name: "Alice Johnson",
email: "alice.j@example.com",
age: 30,
address: {
street: "123 React Lane",
city: "Devville",
zip: "90210",
},
};
function UserCard({ user }: { user: typeof demoUser }) {
if (!user) return <p>No user data.</p>;
return (
<article style={{ border: "1px solid #ccc", padding: 16, borderRadius: 8 }}>
<h2>{user.name}</h2>
<p>Email: {user.email}</p>
<p>Age: {user.age}</p>
<p>
Address: {user.address.street}, {user.address.city}, {user.address.zip}
</p>
{/* Debug view – formatted JSON */}
<pre style={{ background: "#f7f7f7", padding: 12 }}>
{JSON.stringify(user, null, 2)}
</pre>
</article>
);
}
export default function App() {
return (
<main style={{ fontFamily: "sans‑serif", textAlign: "center" }}>
<h1>React Child Error Demo</h1>
<UserCard user={demoUser} />
{/* Incorrect usage (commented to avoid crash) */}
{/* <div>{demoUser}</div> */}
</main>
);}
Highlights:
- Shows both correct and incorrect patterns.
- Inline styling avoids external CSS to keep the example self‑contained.
Also Read: Top React Best Practices for Scalable Frontends
Common Pitfalls & How to Fix Them
| Pitfall |
Example |
Safe Fix |
| Rendering entire API response |
<div>{apiData}</div> |
<div>{apiData.title}</div> or JSON.stringify(apiData) |
| Forgetting to map array |
{users} where users is [{},{}] |
{users.map(u => <UserCard key={u.id} user={u} />)} |
| Directly rendering Date |
<span>{new Date()}</span> |
<span>{new Date().toISOString()}</span> |
Conclusion
Remember:
-
Never drop a raw object in JSX.
-
Extract the primitives you need or serialise the data for display.
-
Use mapping or formatting helpers to keep components clean, readable, and error‑free.