JSX.Element vs ReactElement vs ReactNode
Tags: javascript, react, webdev, beginners
Published: 2022-02-14 19:34:32

These three types usually confuse novice React developers. It seems like they are the same thing, just named differently.
But it's not quite right.

## `JSX.Element` vs `ReactElement`
Both types are the result of `React.createElement()`/`jsx()` function call.

They are both objects with: 
- type
- props
- key
- a couple of other "hidden" properties, like ref, $$typeof, etc

### `ReactElement`
`ReactElement` type is the most basic of all. It's even defined [in React source code](https://github.com/facebook/react/blob/v17.0.0/packages/shared/ReactElementType.js#L15) using flow!
```js
// ./packages/shared/ReactElementType.js

export type ReactElement = {|
  $$typeof: any,
  type: any,
  key: any,
  ref: any,
  props: any,
  // ReactFiber
  _owner: any,

  // __DEV__
  _store: {validated: boolean, ...},
  _self: React$Element<any>,
  _shadowChildren: any,
  _source: Source,
|};
```

This type is also defined [in DefinitelyTyped package](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts#L146).
```ts
interface ReactElement<P = any, T extends string | JSXElementConstructor<any> = string | JSXElementConstructor<any>> {
  type: T;
  props: P;
  key: Key | null;
}
```

### `JSX.Element`
It's more generic type. [The key difference is that `props` and `type` are typed as `any` in `JSX.Element`](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts#L3078).
```ts
declare global {
  namespace JSX {
    interface Element extends React.ReactElement<any, any> { }
    // ...
  }
}   
```
This gives flexibility in how different libraries implement JSX.
For example, [Preact has its own implementation with different API](https://github.com/preactjs/preact/blob/master/src/jsx.d.ts#L18).

## `ReactNode`
`ReactNode` type is a different thing. It's not a return value of `React.createElement()`/`jsx()` function call.
```jsx
const Component = () => {
  // Here it's ReactElement
  return <div>Hello world!</div>
}

// Here it's ReactNode
const Example = Component();
```

React node itself is a representation of the virtual DOM. So `ReactNode` is the set of all possible return values of a component.

```ts
type ReactChild = ReactElement | ReactText;

type ReactFragment = {} | Iterable<ReactNode>;

interface ReactPortal extends ReactElement {
  key: Key | null;
  children: ReactNode;
}

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

## What to use for `children`?
Generally speaking, `ReactNode` is the correct way to type the `children` prop. It gives the most flexibility while maintaining the proper type checking.

But it has a caveat, because `ReactFragment` allows a `{}` type.
```tsx
const Item = ({ children }: { children: ReactNode }) => {
  return <li>{children}</li>;
}

const App = () => {
  return (
    <ul>
      // Run-time error here, objects are not valid children!
      <Item>{{}}</Item>
    </ul>
  );
}
```

---

P.S. [Follow me on Twitter](https://twitter.com/fromaline) for more content like this!

{% twitter 1490749477346553861 %}