How to Display Object Keys and Values in a Loop in React
Advertisement
🧩 Scenario
Architecture Walkthrough
Why Objects Cannot Be Rendered Directly
React renders only specific types: strings, numbers, booleans, null, undefined, React elements, and arrays of these. A plain JavaScript object is none of these. Attempting to embed an object in JSX ({userObject}) throws a runtime error: "Objects are not valid as a React child." You must convert the object to an array before mapping it to JSX.
JavaScript provides three static methods on Object for this: Object.keys(obj) returns an array of the object's own enumerable property names, Object.values(obj) returns an array of the values, and Object.entries(obj) returns an array of [key, value] pairs. All three produce plain arrays, which can be passed directly to map().
Choosing the Right Method
Object.entries() is the most common choice for rendering because it provides both the key and value in one destructured pair, with no need for two separate accesses. Object.keys() is appropriate when you only need to list the property names or when you need the key to look up values from a different source. Object.values() is appropriate when you need only the values (a list of prices, a set of flags) and the key names are irrelevant to the display.
Object keys are guaranteed to be unique within a single object by definition, making them safe to use as the key prop in the resulting map() call.
Handling Nested Objects
When object values are themselves objects or arrays, the render strategy depends on the depth and shape. For shallow nesting, you can render a secondary Object.entries() call inline. For deep or recursive structures, extract a recursive component or flatten the object first with a utility like Object.entries() recursion before rendering.
Key Code Explained
// Object.entries(): key and value together
interface UserProfile {
name: string;
email: string;
country: string;
plan: string;
}
function ProfileSummary({ profile }: { profile: UserProfile }) {
return (
<dl className="profile-grid">
{Object.entries(profile).map(([key, value]) => (
// Object keys are unique within an object — safe as key prop
<div key={key} className="profile-row">
<dt className="label">{key}</dt>
<dd className="value">{String(value)}</dd>
</div>
))}
</dl>
);
}
// Object.keys(): only the keys needed
interface Permissions {
canRead: boolean;
canWrite: boolean;
canDelete: boolean;
canAdmin: boolean;
}
function PermissionList({ permissions }: { permissions: Permissions }) {
const granted = Object.keys(permissions).filter(
(key) => permissions[key as keyof Permissions],
);
if (granted.length === 0) return <p>No permissions granted.</p>;
return (
<ul>
{granted.map((perm) => (
<li key={perm}>{perm}</li>
))}
</ul>
);
}
// Object.values(): only the values needed
interface PricingTier {
monthly: number;
yearly: number;
lifetime: number;
}
function PriceList({ pricing }: { pricing: PricingTier }) {
return (
<ul>
{Object.values(pricing).map((price, index) => (
// Values may not be unique — index is acceptable here since the list is static
<li key={index}>${price}</li>
))}
</ul>
);
}
// Nested object: render sub-entries inline
interface Config {
database: { host: string; port: number };
cache: { ttl: number; maxSize: number };
}
function ConfigViewer({ config }: { config: Config }) {
return (
<div>
{Object.entries(config).map(([section, settings]) => (
<section key={section}>
<h3>{section}</h3>
<ul>
{Object.entries(settings).map(([key, value]) => (
<li key={key}>
<strong>{key}:</strong> {String(value)}
</li>
))}
</ul>
</section>
))}
</div>
);
}
// WRONG: rendering an object directly in JSX
const user = { name: 'Ghazi', email: 'ghazi@example.com' };
// This throws: "Objects are not valid as a React child"
return <div>{user}</div>;
// CORRECT: convert first
return (
<div>
{Object.entries(user).map(([key, value]) => (
<p key={key}>{key}: {value}</p>
))}
</div>
);
In ProfileSummary, String(value) is used to safely convert any value type to a string. Without this, if a value is a boolean or number, React renders it directly (which works), but if it is an object or undefined, the render throws. Converting to string defensively handles mixed-type objects without conditional rendering for every field.
Tradeoffs
| Method | Returns | Use when |
|---|---|---|
| Object.entries() | [key, value][] | Need both key and value in the render |
| Object.keys() | string[] | Need only keys, or to look up values elsewhere |
| Object.values() | value[] | Need only values, key names are irrelevant |
| for...in | (statement) | Avoid in JSX — use the above methods instead |
What Interviewers Actually Check
- Whether you know
Object.entries()as the primary method for rendering key-value pairs - Whether you know all three conversion methods and can choose the right one
- Whether you can explain why
{userObject}throws an error in React - Whether you use object keys as the
keyprop correctly - Whether you can handle nested objects without deeply nesting JSX
Follow-Up Questions
- How does
Object.entries()handle inherited properties vsfor...in? Why does this matter for React rendering? - How would you render a deeply nested configuration object with unknown depth using a recursive React component?
- How does
Mapdiffer from a plain object for React rendering, and can you useMap.entries()the same way? - When would you prefer transforming an object to an array of objects before the component renders (in a selector or useMemo) vs converting inline in JSX?
- How does TypeScript's
keyofoperator help when accessing object values dynamically in a typed component?
Common Candidate Mistakes
- Embedding an object directly in JSX and not knowing why React throws "Objects are not valid as a React child"
- Not knowing
Object.entries()and manually building two separate arrays withObject.keys()plus bracket notation - Using
for...ininside JSX, not realizing it is a statement and cannot produce JSX output inline - Not knowing that
for...initerates over inherited properties whileObject.entries()does not - Using
Object.values()as the key source and getting duplicate key warnings when multiple values are the same
Interview Readiness Checklist
Before you leave this question, make sure you can answer:
- Can you use
Object.entries()to render both key and value from an object as a list? - Can you choose between
Object.keys(),Object.values(), andObject.entries()based on what the render needs? - Can you explain why
{userObject}throws an error in React? - Can you use the object key as the
keyprop in the map() result? - Can you handle a nested object with a secondary
Object.entries()call inside the render?
Summary
React cannot render plain JavaScript objects. Embedding an object directly in JSX throws "Objects are not valid as a React child." To render object data, convert it to an array first using one of three static Object methods, then map the array to JSX.
Object.entries(obj) returns an array of [key, value] pairs and is the most useful for rendering key-value layouts. Object.keys(obj) returns an array of property names and is appropriate when you only need the keys or need to filter by them. Object.values(obj) returns an array of the values when the property names are irrelevant to the display. All three produce arrays that can be passed directly to map(), with object keys being unique by definition and safe to use as the key prop.
For nested objects, apply a secondary Object.entries() call inside the outer map for shallow nesting. For deeply nested or recursively structured data, extract a recursive component or flatten the structure before rendering to avoid deeply nested JSX that is difficult to read and maintain.
Can I map directly over an object in JSX?
No. React cannot render plain objects. You must first convert the object to an array using Object.keys(), Object.values(), or Object.entries(), then use map() on the resulting array.
Advertisement
Stay Updated
Get the latest frontend challenges, interview questions and tutorials delivered to your inbox.
Advertisement