preactjs/preact · critical · Error
Undefined component passed to createElement() You likely fo
Error message
Undefined component passed to createElement()
You likely forgot to export your component or might have mixed up default and named imports${serializeVNode(vnode)}
${getOwnerStack(vnode)} What it means
Thrown from options._diff when a vnode's type is strictly undefined. In Preact the vnode type is the component function/class or tag string; undefined means createElement received nothing usable as the first argument. The message points at a missing export or a default/named import mismatch, and it appends the serialized vnode and the owner component stack for diagnosis.
Source
Thrown at debug/src/debug.js:148
}
if (!isValid) {
let componentName = getDisplayName(vnode);
throw new Error(
`Expected a valid HTML node as a second argument to render. Received ${parentNode} instead: render(<${componentName} />, ${parentNode});`
);
}
if (oldRoot) oldRoot(vnode, parentNode);
};
options._diff = vnode => {
let { type } = vnode;
hooksAllowed = true;
if (type === undefined) {
throw new Error(
'Undefined component passed to createElement()\n\n' +
'You likely forgot to export your component or might have mixed up default and named imports' +
serializeVNode(vnode) +
`\n\n${getOwnerStack(vnode)}`
);
} else if (type != null && typeof type == 'object') {
if (type._children !== undefined && type._dom !== undefined) {
throw new Error(
`Invalid type passed to createElement(): ${type}\n\n` +
'Did you accidentally pass a JSX literal as JSX twice?\n\n' +
` let My${getDisplayName(vnode)} = ${serializeVNode(type)};\n` +
` let vnode = <My${getDisplayName(vnode)} />;\n\n` +
'This usually happens when you export a JSX literal and not the component.' +
`\n\n${getOwnerStack(vnode)}`
);
}
throw new Error(View on GitHub (pinned to e881e7e838)
Solutions
- Check the source module: confirm the component is exported with the matching syntax — `export default function Foo` vs `export function Foo`.
- Adjust the import to match: use `import Foo from` for default, `import { Foo } from` for named, or `import Foo, { Bar } from` for both.
- For ESM/CJS interop use `import pkg from './mod'; const Foo = pkg.default || pkg`.
- Eliminate circular imports — log the imported value at module top to confirm it is defined before render.
Example fix
// before — Foo.js has `export default function Foo()`
import { Foo } from './Foo'; // Foo is undefined
render(<Foo />, root);
// after
import Foo from './Foo';
render(<Foo />, root); Defensive patterns
Strategy: type-guard
Validate before calling
function assertComponent(component, name) {
if (component === undefined || component === null) {
throw new Error(`Component "${name}" is ${String(component)}. Check the export/import style.`);
}
if (typeof component !== 'function') {
throw new Error(`Expected a component function/class for "${name}", got ${typeof component}.`);
}
return component;
}
import * as Mod from './Foo';
const Foo = assertComponent(Mod.default ?? Mod.Foo, 'Foo');
render(<Foo />, root); Type guard
/** @param {unknown} c */
function isPreactComponentType(c) {
return typeof c === 'function' || typeof c === 'string';
} Prevention
- Match import syntax to export syntax precisely (default vs named).
- For ESM/CJS interop use `const C = mod.default ?? mod`.
- Eliminate circular imports — they often resolve to undefined at first evaluation.
- Add an eslint rule (import/no-named-as-default-member, import/no-cycle) to catch these statically.
When it happens
Trigger: Named import where the module uses default export: import { Foo } from './Foo' when Foo is exported default; default import where the module uses a named export: import Foo from './Foo' when Foo is a named export; circular import that evaluates to undefined at first use; barrel file (index.js) re-exporting a component that has not yet been defined; dynamic import used synchronously before resolution.
Common situations: Interop between ESM and CommonJS where default becomes { default: Component }; tree-shaking misconfiguration stripping a used export; HMR stale module where the replaced module lost the export; conditional export that resolves to undefined in the current environment; forgetting to add `export` to a function component declaration.
Related errors
- Invalid type passed to createElement(): ${type} Did you acc
- Invalid type passed to createElement(): ${Array.isArray(type
- Objects are not valid as a child. Encountered an object with
- Undefined parent passed to render(), this is the second argu
- Expected a valid HTML node as a second argument to render. R
AI-assisted analysis of preactjs/preact@e881e7e838 (2026-08-13).
Data as JSON: /api/errors/91e2129f27228e98.
Report an issue: GitHub.