JSX.Element vs React.ReactNode Comparison
Recently while looking at antd source code, I noticed these two type definitions for render elements: JSX.Element and React.ReactNode. I was curious about their differences, so I’m documenting it here.
First, the big conclusion [commonly known as stating the obvious]: they are not the same.
Are JSX.Element and React.ReactNode equivalent?
Create a Card component with the content parameter defined as JSX.Element. If you pass an array element as a parameter, it directly reports an error. Changing it to JSX.Element[]
or React.ReactNode
both work fine.
Does this mean they are equivalent?
Are JSX.Element[] and React.ReactNode equivalent?
No, the ReactNode type definition is as follows:
type ReactNode = ReactChild | ReactFragment | ReactPortal | string | number | boolean | null | undefined;
This means it can accept various types, while Element must be a React Element [of course, it can be a native Element].
interface ReactElement<P> {
type: string | ComponentClass<P> | SFC<P>;
props: P;
key: Key | null;
}
For example, in the above case, if the type is defined as JSX.Element and content is a number, it will report a type error.
Technical Explanation
Quoted from an expert’s answer:
Quote @ferdaber: A more technical explanation is that a valid React node is not the same thing as what is returned by React.createElement. Regardless of what a component ends up rendering, React.createElement always returns an object, which is the JSX.Element interface, but React.ReactNode is the set of all possible return values of a component.
- JSX.Element -> Return value of React.createElement
- React.ReactNode -> Return value of a component
Type Definitions in antd
Understanding the differences, looking back at antd’s usage of these types makes more sense. For example, the commonly used Table component uses different type definitions for the render method and renderColumnTitle. Why? Because their situations are indeed different.
render uses JSX.Element
Because the table component must ultimately return a component element; it cannot be a number, string, etc.
renderColumnTitle uses React.ReactNode
As shown above, renderColumnTitle might return just a number, string, etc., not necessarily an element object.
Final Thoughts
Using type definitions correctly and appropriately can prevent some bugs and also improve code readability. The extensive use of TypeScript makes me feel that types truly serve as part of the documentation.