What happened
You're passing something as a JSX component โ a variable, a prop, something pulled from a map โ and TypeScript refuses to compile it. The error looks like this:
JSX element type 'Component' does not have any construct or call signatures. ts(2604)
This shows up most in dynamic rendering scenarios: storing a component in a variable, receiving it as a prop, or picking it from a lookup table. The value works fine at runtime โ React handles it without complaint. TypeScript just can't figure out what type it is, so it blocks the build.
Why TypeScript throws this
For JSX to be valid, TypeScript must confirm the element is either a function component โ (props) => JSX.Element โ or a class component that extends React.Component. When the type is any, object, unknown, or some unrelated interface, that check fails. TypeScript has no way to know the value is renderable.
Reproducing the error
Simplest example:
// โ Breaks
const Component = someMap[key]; // inferred as `object` or `any`
return <Component />; // ts(2604)
A slightly sneakier version โ wrong prop type annotation:
// โ Wrong prop type
interface Props {
icon: object; // just a plain object to TypeScript, not a component
}
const Wrapper = ({ icon: Icon }: Props) => {
return <Icon />; // ts(2604)
};
Fix 1 โ Use the correct component prop type
When a prop is supposed to be a component, type it as React.ComponentType or React.ElementType. Not object, not any.
import React from 'react';
interface Props {
icon: React.ComponentType; // โ
covers both function and class components
// need to pass props to it? Be explicit:
icon: React.ComponentType<{ size?: number }>;
}
const Wrapper = ({ icon: Icon }: Props) => {
return <Icon />; // โ
};
Need to support HTML tags like "div" or "span" too? Use React.ElementType instead โ it covers both components and native elements:
interface Props {
as?: React.ElementType; // 'div', 'span', MyComponent โ all valid
}
const Box = ({ as: Tag = 'div', children }: React.PropsWithChildren<Props>) => {
return <Tag>{children}</Tag>; // โ
};
Fix 2 โ Annotate your component maps
Pulling a component out of a map? Annotate the map itself so TypeScript knows every value is a component:
import React from 'react';
const componentMap: Record<string, React.ComponentType> = {
home: HomeIcon,
user: UserIcon,
settings: SettingsIcon,
};
const Component = componentMap[key]; // now typed as React.ComponentType
return <Component />; // โ
Stuck with a poorly-typed value from a third-party library you can't modify? A type assertion works as a last resort โ but know that it removes type safety for that variable:
const Component = someMap[key] as React.ComponentType;
return <Component />; // โ
โ use this sparingly
Fix 3 โ Rename the variable to PascalCase
This one catches people off guard. JSX treats lowercase variable names as HTML tags, not components. Even if the type is 100% correct, a lowercase name will cause problems:
// โ React sees this as an HTML tag named "component"
const component = MyComponent;
return <component />;
// โ
PascalCase tells React it's a component
const Component = MyComponent;
return <Component />;
Destructuring from props? Rename right there in the destructure:
const { icon: Icon } = props;
return <Icon />; // โ
Fix 4 โ Check your class component declaration
Writing a class component? It must extend React.Component. Without it, TypeScript sees it as a plain class โ no call signatures, no construct signatures, just an object:
// โ Missing extends โ TypeScript won't treat this as a component
class MyButton {
render() {
return <button>Click</button>;
}
}
// โ
class MyButton extends React.Component {
render() {
return <button>Click</button>;
}
}
Fix 5 โ Check tsconfig.json when all JSX is broken
ts(2604) appearing on every JSX element, not just the dynamic ones? That points to a config issue rather than a type issue. Check the jsx field in tsconfig.json:
{
"compilerOptions": {
"jsx": "react-jsx", // React 17+ โ no manual import needed
// "jsx": "react" for older setups
"lib": ["dom", "esnext"]
}
}
Verify the fix
Run the TypeScript compiler directly โ zero errors means you're clean:
# Typecheck without emitting files
npx tsc --noEmit
# Next.js or Vite project
npm run build
In VS Code, hover over the component variable after the fix. You should see React.ComponentType<...>, not object or any. If you still see a vague type, the annotation hasn't propagated correctly yet.
Quick reference
- Component as prop โ type it as
React.ComponentTypeorReact.ElementType - Component from a map โ annotate the map as
Record<string, React.ComponentType> - Lowercase variable โ rename to PascalCase before using in JSX
- Class component โ confirm it
extends React.Component - Every JSX element broken โ check the
jsxfield intsconfig.json
Lessons learned
ts(2604) is TypeScript catching a real category of bug: accidentally rendering non-components. The fix is almost never a logic problem โ it's a missing or incorrect type annotation, usually on a prop or a map value.
Two things worth committing to memory. First, type component props as React.ComponentType, not object. Second, always rename destructured component props to PascalCase before using them in JSX. Lowercase names in JSX silently become HTML tags โ TypeScript will flag it, but the error message won't always make that obvious.

