preactjs/preact · error · Error
Objects are not valid as a child. Encountered an object with
Error message
Objects are not valid as a child. Encountered an object with the keys {${keys}}.
${getOwnerStack(vnode)} What it means
Thrown from options.diffed after a vnode's children are resolved: if any rendered child is a plain object (typeof 'object', truthy) with no `type` property, Preact rejects it. Valid children are primitives (string/number), arrays, booleans/null/undefined (ignored), or vnodes (which carry a `type`). A bare object literal like `{foo:1}` as a child is not renderable. The check runs in diffed (not vnode) so component children that intentionally accept object shapes are not falsely flagged — only actually-rendered plain objects are caught.
Source
Thrown at debug/src/debug.js:339
// eslint-disable-next-line
vnode.__proto__ = deprecatedProto;
if (oldVnode) oldVnode(vnode);
};
options.diffed = vnode => {
const { type, _parent: parent } = vnode;
// Check if the user passed plain objects as children. Note that we cannot
// move this check into `options.vnode` because components can receive
// children in any shape they want (e.g.
// `<MyJSONFormatter>{{ foo: 123, bar: "abc" }}</MyJSONFormatter>`).
// Putting this check in `options.diffed` ensures that
// `vnode._children` is set and that we only validate the children
// that were actually rendered.
if (vnode._children) {
vnode._children.forEach(child => {
if (typeof child === 'object' && child && child.type === undefined) {
const keys = Object.keys(child).join(',');
throw new Error(
`Objects are not valid as a child. Encountered an object with the keys {${keys}}.` +
`\n\n${getOwnerStack(vnode)}`
);
}
});
}
if (vnode._component === currentComponent) {
renderCount = 0;
}
if (
typeof type === 'string' &&
(isTableElement(type) ||
type === 'p' ||
type === 'a' ||
type === 'button')
) {View on GitHub (pinned to e881e7e838)
Solutions
- Render a specific property: `<div>{user.name}</div>` rather than the whole object.
- Serialize explicitly: `<pre>{JSON.stringify(obj, null, 2)}</pre>` for debugging.
- For lists, map to elements: `items.map(i => <li key={i.id}>{i.label}</li>)`.
- If the object is a vnode-like wrapper, ensure it has a `type` — otherwise wrap it in a component.
Example fix
// before
function Greeting({ user }) {
return <div>Hello {user}</div>; // user is an object
}
// after
function Greeting({ user }) {
return <div>Hello {user.name}</div>;
} Defensive patterns
Strategy: type-guard
Validate before calling
function sanitizeChildren(children) {
const arr = Array.isArray(children) ? children : [children];
return arr.map(c => {
if (c != null && typeof c === 'object' && !('type' in c) &&
!Array.isArray(c) && !(c instanceof Date) && typeof c !== 'boolean') {
return JSON.stringify(c);
}
return c;
});
}
// before render
return <div>{sanitizeChildren(maybeObjectChild)}</div>; Type guard
/** @param {unknown} c */
function isRenderableChild(c) {
if (c == null || typeof c === 'boolean') return true; // ignored
if (typeof c === 'string' || typeof c === 'number') return true;
if (Array.isArray(c)) return c.every(isRenderableChild);
if (typeof c === 'object') return 'type' in c; // vnode
return false;
} Prevention
- Always access a specific property when interpolating an object: `{user.name}` not `{user}`.
- Serialize objects explicitly with JSON.stringify when debugging.
- Map data arrays to elements with keys; never render a raw object array.
- Type children strictly in TS: `ReactNode`/`ComponentChild` to surface object children at compile time.
When it happens
Trigger: Passing an object literal as a child: `<div>{ {foo:1} }</div>`; interpolating a state object directly: `<span>{user}</span>` where user is an object; returning an object from a map callback into children: `items.map(i => ({id: i.id}))`; spreading a props object into children by mistake.
Common situations: Forgetting to access a property: `<div>{user}</div>` instead of `<div>{user.name}</div>`; passing a Map/Set/Error object as a child expecting implicit toString; mock data shaped as objects fed directly into JSX; converting an API response object to children without serialization.
Related errors
- Undefined component passed to createElement() You likely fo
- Invalid type passed to createElement(): ${type} Did you acc
- Undefined parent passed to render(), this is the second argu
- Expected a valid HTML node as a second argument to render. R
- Invalid type passed to createElement(): ${Array.isArray(type
AI-assisted analysis of preactjs/preact@e881e7e838 (2026-08-13).
Data as JSON: /api/errors/7948a7fb9216a614.
Report an issue: GitHub.