JSX Element, React Node, React Element
Tags: javascript, beginners, react, typescript
Published: 2022-03-12 00:12:52

I am currently migrating a React application to TypeScript. So far, this works pretty well, but I have a problem with the return types of my `render` functions, specifically in my functional components.

Cut a long story short, I have these questions during my learning process:

1. What is the difference between `JSX.Element`, `ReactNode` and `ReactElement`?
2. Why do the `render` methods of class components return `ReactNode`, but functional components return `ReactElement`?

## What is the difference between `JSX.Element`, `ReactNode` and `ReactElement`?

A ReactElement is an object with a type and props.

```jsx
type Key = string | number

 interface ReactElement<P = any, T extends string | JSXElementConstructor<any> = string | JSXElementConstructor<any>> {
    type: T;
    props: P;
    key: Key | null;
}
```

A ReactNode is a ReactElement, a ReactFragment, a string, a number or an array of ReactNodes, or null, or undefined, or a boolean:

A ReactNode is a ReactElement, a ReactFragment, a string, a number or an array of ReactNodes, or null, or undefined, or a boolean:

```jsx
type ReactText = string | number;
type ReactChild = ReactElement | ReactText;

interface ReactNodeArray extends Array<ReactNode> {}
type ReactFragment = {} | ReactNodeArray;

type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;
```

JSX.Element is a ReactElement, with the generic type for props and type being any. It exists, as various libraries can implement JSX in their own way, therefore JSX is a global namespace that then gets set by the library, React sets it like this:

```jsx
declare global {
  namespace JSX {
    interface Element extends React.ReactElement<any, any> { }
  }
}
```

By example:

```jsx
<p> // <- ReactElement = JSX.Element
   <Custom> // <- ReactElement = JSX.Element
     {true && "test"} // <- ReactNode
  </Custom>
 </p>
```

## Why do the `render` methods of class components return `ReactNode`, but functional components return `ReactElement`?

they do return different things. `Components` return:

```jsx
render(): ReactNode;
```

**tl;dr:** It is a current TS type incompatibility [not related to core React](https://github.com/microsoft/TypeScript/issues/21699).

- TS class component: returns `ReactNode` with `render()`, more permissive than React/JS
- TS function component: returns `JSX.Element | null`, more restrictive than React/JS

In principle, `render()` in React/JS class components [supports the same return types](https://github.com/facebook/react/issues/12155#issuecomment-363089187) as a function component. With regard to TS, the different types are a type inconsistency still kept due to historical reasons and the need for backwards compatibility.

Ideally a [valid return type](https://reactjs.org/docs/react-component.html#render) would probably look more like this:

```jsx
type ComponentReturnType = ReactElement | Array<ComponentReturnType> | string | number
  | boolean | null // Note: undefined is invalid
```

If you guys have other thoughts leave a comment I'll update the post base on your solutions as well.. cheers🍻