Introduction to DOM Elements and Polymorphic Components

Learn about the abstraction and polymorphism for optimizing web component development.

Component abstraction

In most of the larger applications and projects we’ve worked on, we often build many supersets or abstraction components on top of the standard HTML elements. Some examples include custom button elements that might take a prop defining whether or not that button should be primary or secondary, or maybe one that indicates that it will invoke a dangerous action, such as deleting or removing an item from the database. We still want our button to have all the properties of a button and the props we want to add to it.

Another common case is that we’ll create a component that allows us to define a label and an input field at once. We don’t want to re-add all the properties an <input /> element takes. We want our custom component to behave just like an input field but also take a string for the label and automatically wire up the htmlFor prop on the <label /> to correspond with the ID on the <input />.

In JavaScript, we can use {...props} to pass through any props to an underlying HTML element. This can be a bit trickier in TypeScript, where we need to explicitly define what props a component will accept. While it’s nice to have fine-grained control over the exact types that my component accepts, it can be tedious to manually add type information for every prop.

In certain scenarios, we need a single adaptable component, like a <div>, that changes styles according to the current theme. For example, maybe we want to define what styles should be used depending on whether or not the user has manually enabled light or dark mode for the UI. We don’t want to redefine this component for every single block element (such as <section>, <article>, <aside>, and so on). It should be capable of representing different semantic HTML elements, with TypeScript automatically adjusting to these changes.

Strategies for component development

There are a few strategies that we can employ.

  • For components where we’re creating an abstraction over just one kind of element, we can extend the properties of that element.

  • For components where we want to define different elements, we can create polymorphic components. A polymorphic component is a component designed to render as different HTML elements or components while maintaining the same properties and behaviors. It allows us to specify a prop to determine its rendered element type. Polymorphic components offer flexibility and reusability without us having to reimplement the component. For a concrete example, you can look at Radix’s implementation of a polymorphic componenthttps://www.radix-ui.com/primitives/docs/utilities/polymorphic.

Get hands-on with 1400+ tech skills courses.