If you’re working with TypeScript and React, you’ve probably seen types like JSX.Element, React.ReactNode, and React.ReactElement floating around. Maybe you’ve even thrown one in just to make the red squiggle go away. 😬
🤔 So… What Are These Types?
Type | What..? | When..? |
---|---|---|
JSX.Element | What JSX like compiles to | When returning from a React component |
React.ReactElement | The object returned by React.createElement | When you need type + props of a JSX node |
React.ReactNode | Anything React can render | Use for children or render content |
Now, let's dig deep.
1. JSX.Element – The Immediate Output of JSX
When you write something like:
const element = <h1>Hello, world!</h1>;
The type of this element
is JSX.Element. That’s what the JSX compiles to under the hood: a JavaScript object representing that virtual DOM node.
const MyComponent = (): JSX.Element => {
return <span>I am a JSX.Element</span>;
};
🔍 Use JSX.Element when you're typing a component return value. It’s the default. The safe bet. Your vanilla latte.
2. React.ReactElement – JSX with Metadata
A ReactElement is a more concrete object. It includes:
- The type of component (e.g. 'div' or MyComponent)
- The props passed to it
- Potentially, keys and refs
const element: React.ReactElement = React.createElement('p', null, 'Hey there');
🔍 Use React.ReactElement when you’re dealing with the React element object itself, not what you return from a component.
React.ReactNode – The Big Daddy of Renderables
This one’s wild. React.ReactNode can be:
- A JSX.Element
- ReactElement
- A string
- A number
- null, undefined
- false (yes, false!)
- Arrays of all the above
- A fragment or a portal
It represents anything React can render, making it the go-to for children props or any UI content.
Example:
type MyProps = {
children: React.ReactNode;
};
const Wrapper = ({ children }: MyProps) => (
<div className="wrapper">{children}</div>
);
So you can do this:
<Wrapper>
<h1>Hello</h1>
Just some text
{[<span key="1">One</span>, <span key="2">Two</span>]}
</Wrapper>
🔍 Use React.ReactNode when accepting anything renderable as a prop. Especially children.
🧠 When to Use What?
- Use JSX.Element when defining the return type of your functional component.
- Use React.ReactNode when accepting children or rendering content.
- Use React.ReactElement when dealing with React elements as objects, e.g. for rendering engines, inspection, or cloning.
🧙♀️ Bonus: Inspect a ReactElement
If you're building tooling (hello, component docs or previewers), you might want to peek into the props:
function debug(el: React.ReactElement) {
console.log("Component type:", el.type);
console.log("Props:", el.props);
}
🔚 Closing Thoughts
Knowing the difference makes your components more robust, your APIs cleaner, and your mental model of React deeper.
So next time you hit children: any, stop. Breathe. And reach for React.ReactNode.
📚 References
- TypeScript Handbook – JSX
React Type Definitions (DefinitelyTyped GitHub)
ReactNode definition: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts#L69
- ReactElement definition: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts#L270
- React Docs – JSX In Depth
React Docs – React.createElement
“React Children Type in TypeScript” by Kent C. Dodds
Top comments (0)