Create more extensible React components with the “as” prop pattern
React components bake HTML semantics into their implementation. Take this Title
component example:
While this isn’t a particularly interesting or robust component, it will help illustrate a useful concept. This component creates an HTML h1
element, applies a className
prop set to "title"
to style it, and then allows the consumer of the component to pass along whatever content they’d like to render using the children
prop. In this case, the semantics is the heading level 1 expressed as an h1
element.
The component can then be used like so:
We can expect the Demo
to yield something to the effect of:
Based on this, we can clearly see the Demo
creates a section
element and the Title
an h1
. For this simple, albeit contrived use case, this works fine.
What do we do when we need our Title
component to render something else? We might need it to render as an h2
or perhaps a plain div
. We often want this flexibility when optimizing for SEO or to improve the accessibility of a page (conveniently improving one of these often improves the other). Whatever our reason, how do we handle this? Creating a TitleH2
or a TitleDiv
that does nothing more than swap out the wrapper HTML element with a different one seems far from D.R.Y. As you have likely guessed by now, this is where the as
prop can be utilized to great effect.
The as
prop
Here’s what using the as
prop looks like:
The as
prop is instructing the component (Title
) to render its wrapper element as something else. If we don’t specify an as
prop, the component will use a default value it has defined internally.
Here’s how it works in code:
Let’s break down what’s happening here. Within the Title
function, we’re destructuring two properties off of props
: as
and children
. Nothing has really changed with the use of children
from the original code other than we’re now using the destructured variable in place of props.children
. The as
prop we’ve also destructured and then aliased to a new constant called Component
. Thanks to defaultProps
, if the consumer of Title
does not specify a value for as
, Component
will evaluate to 'h1'
. In that case, Title
will render:
However, if the user of Title
does specify an as
value, then that becomes what is rendered.
Why alias the as
prop to Component
?
Similar to the name “as”, the name Component
is somewhat arbitrary. The important part is that when you return the prop in the context of a React element (e.g. <Component />
) it must be capitalized. That’s just a rule of React. Lowercase component names are reserved for DOM nodes and come from the react-dom library.
Using as with custom components
A cool thing about the as
prop is that it isn’t limited to HTML elements. You can pass in custom React components without any additional work. Let’s pretend we have a component called SpecialButton
that we wanted to style to appear like a Title
but render as a button.
If we breakdown what this is doing, it looks like this:
Note that in order for the Title
component styles (i.e. className="title"
) to properly forward to the final <button />
, we need to destructure the className
on the SpecialButton
component and then forward the prop along to the final button.
As was the case in SpecialButton
, I find forwarding along the className
prop to be a useful general best practice. I plan to expand upon this in a follow-up post where I will illustrate a more robust way of handling className
and other props you may want to forward along.
So that’s how it all works. There’s nothing special about the as
prop. It's just a prop like any other.