⚛️ Demystifying React's Types: Component types
Tags: typescript, react, webdev, frontend
Published: 2024-03-28 04:57:09

React's type system, when used with TypeScript, provides developers with a robust framework for building type-safe applications. Understanding the nuances between various React types is crucial for making informed decisions about their usage. Let's dive into a detailed comparison of these types in pairs to understand their differences and appropriate use cases.

## 1️⃣ React.FC vs React.ElementType

### React.FC (React.FunctionComponent)

`React.FC` is a type specifically used for defining function components in React. It started to become more popular when React Hooks were introduced and widely adopted.

Prior to React 18, it used to include an implicit `children` prop, making it suitable for components expected to have children. For a long time, though, the implicit `children` prop type has been removed according to [React 18's type changes](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/56210).

Here's a usage example:

```tsx
interface SomeComponentProps {
  title: string;
  children?: React.ReactNode;
}

export const SomeComponent: React.FC<SomeComponentProps> = ({ title, children }) => {
  return (
    <article>
      <h2>{title}</h2>

      {children}
    </article>
  );
};
```

However, since the removal of the implicit `children` prop type, much fewer people use `React.FC` now as it's clunky and provides no real benefits compared to directly assigning the interface to the props object:

```tsx
interface SomeComponentProps {
  title: string;
  children?: React.ReactNode;
}

export const SomeComponent = ({ title, children }: SomeComponentProps) => {
  return (
    <article>
      <h2>{title}</h2>

      {children}
    </article>
  );
};
```

### React.ElementType

`React.ElementType`, on the other hand, is a broader type that represents any component type that can be rendered by React. This includes not only React functional and class components but also string tags for HTML elements (e.g., 'div', 'span'). `React.ElementType` is particularly useful when you want to accept a component as a prop and render it, allowing for dynamic component usage.

```tsx
const DynamicComponent: React.ElementType = 'div';

export const Container = () => (
  <DynamicComponent className="container">Hello, world!</DynamicComponent>
);
```

Here, `DynamicComponent` is typed as `React.ElementType`, allowing it to be dynamically assigned to different types of components or HTML elements.

### Comparison notes

- `React.FC` is mainly for defining functional components. Since React 18, it is not that useful anymore.
- `React.ElementType` offers more flexibility in accepting various kinds of renderable entities. Use `React.ElementType` when you need something that can dynamically accept different types of React components or HTML elements.

## 2️⃣ React.ReactNode vs React.ReactElement vs JSX.Element

### React.ReactElement

`React.ReactElement` is an object with `type`, `props`, and `key` properties, created by the `React.createElement()` function. It's a more specific type compared to `React.ReactNode`, representing elements that can be rendered directly by React.

```tsx
const elementContent: React.ReactElement = <div>Hello, React.ReactElement!</div>;

export const Container = () => <>{elementContent}</>;
```

### React.ReactNode

`React.ReactNode` is the most inclusive type, representing anything that can be rendered by React. This includes primitive types (strings, numbers, booleans), `JSX.Element`s,  `React.ReactElement`s, arrays of these types, and more. It's the go-to type for props that can accept a wide variety of content, such as `children`.

```tsx
const multiElementContent: React.ReactNode = (
  <div>
    <p>This is a paragraph.</p>
    {'This is a text node.'}
    {null}
  </div>
);

const primitiveTypeContent: React.ReactNode = "I'm a primitive-type React.ReactNode";

export const Container = () => {
  return (
    <>
      {multiElementContent}
      {primitiveTypeContent}
    </>
  );
};
```

This Venn diagram below depicts the relation between `React.ReactNode` and `React.ReactElement`:

![React.ReactNode vs React.ReactElement](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rw8khfxekei6wydxqkmb.png)

### JSX.Element

`JSX.Element` is essentially a `React.ReactElement` with a broader definition, allowing various libraries to implement JSX in their own way. It's the type used internally by TypeScript to represent the return type of JSX expressions.

```tsx
const jsxElement: JSX.Element = <span>Hello, JSX.Element!</span>;

export const Container = () => <>{jsxElement}</>;
```

### Comparison notes

- `React.ReactNode` is the most flexible and inclusive, suitable for typing props like children that can accept diverse content.
- `React.ReactElement` and `JSX.Element` are more specific, with `React.ReactElement` being suitable for elements created by React and `JSX.Element` for elements defined using JSX syntax.

## 💡 Fun facts

- The returned value of a function component a.k.a. the result of the rendering process is always either `React.ReactNode` or `React.ReactElement`/`JSX.Element`. Basically, a function component can be understood as:

```tsx
type ReactFC<P = {}> = (props: P) => React.ReactNode;
```

- Calling the function component just like any regular function gives us the same result as using the JSX syntax, meaning:

```tsx
const MyComponent = ({ children }: { children: React.ReactNode }) => {
  return <div>{children}</div>;
};

export const App = () => {
  return (
    <div>
      <MyComponent>
        Rendering MyComponent with <strong>JSX Syntax</strong>
      </MyComponent>
    </div>
  );
}
```

is the same as:

```tsx
const MyComponent = ({ children }: { children: React.ReactNode }) => {
  return <div>{children}</div>;
};

export const App = () => {
  return (
    <div>
      {MyComponent({
        children: (
          <>
            Rendering MyComponent by&nbsp;
            <strong>Invoking Function Component</strong>
          </>
        ),
      })}
    </div>
  );
}
```

Check out the codesandbox below for the demo:

{% codesandbox invoking-react-function-component-lkz7vd %}

## 🏁 Conclusion

Understanding the differences between these React types and interfaces allows developers to make more informed decisions, leading to cleaner, more maintainable code. Whether you're defining components, accepting dynamic content, or handling children, choosing the right type is crucial for leveraging React and TypeScript's full potential.

If you want to explore how to optimally organize the code in a React Component, make sure to check this this article:

{% link https://dev.to/itswillt/organizing-code-in-a-react-component-4coa %}

Please look forward to my upcoming articles that dive deeper into the patterns that utilize the understanding of these types.

---

If you're interested in Frontend Development and Web Development in general, follow me and check out my articles in the profile below.

{% user itswillt %}