2021-12-25 React Typescript

React: Creating a Heading component leveraging Typescript’s typing system

In this blog post, I intend to demonstrate how to implement a Heading component that given a prop value, renders a heading element. To achieve just that I am going to use Typescript to enforce the component’s API, and React.createElement.

The API

Nowadays it’s common for React’s styling libraries to provide a component that enables developers to define the exact kind of heading element they want the application to render, and on this post, I plan to achieve the same:

<!-- H1 -->
<Heading as="h1">The HTML Section Heading elements</Heading>

<!-- H2 -->
<Heading as="h2">Examples</Heading>

Component structure

import React from "react";

interface Props extends Partial<Omit<HTMLHeadingElement, "children">> {
  children: string;
  as?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
}

export function Heading({ as = "h1", children, style }: Props) {
  return React.createElement(as, { ...style }, children);
}

Firstly, In order to move away from a switch statement with multiple declarations of heading elements, I have decided to use React.createElement API because it enables the developer to pass a string stating which tag should be created.

Secondly, I have used Typescript’s union type feature to enforce the component’s consumer to pass a valid heading element’s name.

Component’s consumer using auto-complete to define the intended heading element.

Extends, Partial and Omit

interface Props extends Partial<Omit<HTMLHeadingElement, "children">> {}

In case you wondered what is this block of code doing, here is the explanation: I needed to inherit all the attributes of heading elements, hence the extends statement. However, I did not want to make them explicitly mandatory, therefore I have used the Partial type to make all the properties defined in HTMLHeadingElement interface optional. I wanted one more thing, which was to override the children property from the interface aforementioned, therefore I have used the Omit type to achieve that.

Conclusion

This was a rather simple implementation of a component leveraging React.createElement(), which is the API behind JSX, and Typescript to enforce an API that enable developers to choose the exact kind of heading element they intend to use.

Further reading