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>;
Enter fullscreen mode Exit fullscreen mode

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>;
};
Enter fullscreen mode Exit fullscreen mode

🔍 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');
Enter fullscreen mode Exit fullscreen mode

🔍 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>
);
Enter fullscreen mode Exit fullscreen mode

So you can do this:

<Wrapper>
  <h1>Hello</h1>
  Just some text
  {[<span key="1">One</span>, <span key="2">Two</span>]}
</Wrapper>
Enter fullscreen mode Exit fullscreen mode

🔍 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);
}
Enter fullscreen mode Exit fullscreen mode

🔚 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

  1. TypeScript Handbook – JSX
  2. https://www.typescriptlang.org/docs/handbook/jsx.html

  3. React Type Definitions (DefinitelyTyped GitHub)

  4. ReactNode definition: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts#L69

  1. React Docs – JSX In Depth
  2. https://reactjs.org/docs/jsx-in-depth.html

  3. React Docs – React.createElement

  4. https://reactjs.org/docs/react-api.html#createelement

  5. “React Children Type in TypeScript” by Kent C. Dodds

  6. https://kentcdodds.com/blog/how-to-type-react-children