Categories
Components Systems

The Tiny Yet Mighty “as” Prop

Component libraries, frameworks, and toolkits share a common goal. That goal is to provide users with the ability to do cool things with less work. G2 Components is no different in that regard!

They can provide power by focusing and abstracting away complexity (like MATH!). They can provide UI cohesion by building upon and encouraging specific usage patterns. They can also provide safety nets by understanding multiple use cases and supporting those use cases with sensible defaults.

One of the biggest challenges (if not the biggest) is to do all of this while being as flexible as possible. As far as React UI is concerned, I can’t think of another pattern that does this better than the humble “as” prop.

Rendering As Anything

The “as” prop pattern allows for a component to render as anything else. It effectively combines the features and behaviours of multiple UI components as one. This is particularly effective when you need the styles of one component, but the functionality of another (I’d argue this is probably the most common problem for UI libraries).

It Starts With a “Box”

An illustration of a “Box” as a paragraph HTML tag.

In order to achieve the full potential of the “as” prop, a UI library/toolkit must use some sort of base component that supports this feature. It’s common these days to have some sort of “Box” component, which is then used to build everything else. Think of it as a smarter alternative to a plain old div. G2 Component’s “Box” component is known as a View.

How “Box” is implemented is important. Very important. After all, it is literally the building block for your entire library/UI layer. It should work just like any default React HTML element (e.g. div, p, span, etc…). That means it knows how to absorb properties like aria attributes, onClick handlers, and so on. Lastly, not only should the “as” prop be able to render “Box” as any other HTML element, it should also be able to render it as any other (custom) React component!

A tall order for such a tiny thing!

Where things get interesting, really interesting, is when you combine multiple components together that both support the “as” prop.

As + As

Illustration of a fushion between G2 and Reakit

If I’m honest, beyond the component library layer, it’s probably rare need to coordinate multiple “as” prop components together. However, in G2, we get to experience this wonderful synergy when the components are composed and combined with the “as” prop supported Reakit library.

For certain components, like Button, the functions, properties, and styles “flow” harmoniously between G2 and Reakit. It ultimately renders an enhanced G2-ified Reakit Button (Style system an all). And since the “as” prop pattern is a principle feature of G2, you can indeed, render (or “hot swap”) the internal G2 Button for anything else!

By the way! Reakit is a UI toolkit that powers the Accessibility system/layer for G2. It was created by the incredible Diego Haz. He’s been instrumental in the ideation and refinement of G2 Components ❤️. (Thanks Diego!)

Hot Swapping

“Hot Swapping” (a term I made up) is a feat that is enabled with the “as” prop pattern. To better illustrate this idea, I’m going to use a hypothetical scenario, one that I’ve experienced far too many times.

Let’s say you’ve meticulously created an Input component of some kind. This awesome Input embodies the aesthetics of your Design System and has pre-built features like rendering a loading spinner if it’s in a loading state.

Now you need this Input to do some fancy things with value formatting, like spacing out numbers for a credit card. You do some research (Google) and you find a library that does exactly what you need! Perhaps react-number-format.

You then realize that your Input can’t render react-number-format . But you also can’t just pluck out certain parsing features from that library.

What do you do…

You abstract away features from Input to make it sharable (and more complicated), and you create a new InputNumberFormat component that is basically a mirror copy of your Input component, except with some HTML elements swapped. And the styles have to be patched for this one edge-case because of reasons.

Alternatively, with the “as” prop, you could have rendered your Input as the one from react-number-format.

Code snippet of a TextInput rendering as react-number-format.

That way, you get all of the fancy features of both components!

GIF demonstrating G2’s TextInput component rendering with number formatting features.

Conclusion

Balancing power and flexibility is a difficult ongoing challenge for folks working at the library/toolkit layer. (I feel like there’s a Spiderman quote in there somewhere). The last thing you’d want is users having to jump through 17 hoops in order to sub-optimally use your thoughtfully crafted components. As such, folks working at these layers (designers and developers and all) must do their best to understand, support, and improve the ways users use their stuff. These often materialize as creative solutions that push the medium forward, establishing new paradigms along the way. In the case of React UI and the “as” prop, this powerful technique evolved from somebody somewhere asking the simple yet brilliant question of…

“Hey! What if we let them render any component as anything else?”

P.S. If you’re curious about G2’s “Box” implementation, click here to see the source code. For context, the code is actually one step lower than the “Box” – it’s a thing that creates a “Box” ✌️📦.

By Q

I specialize in Design Systems, Interaction, and UI.
I'm a Principal Designer at Automattic.

4 replies on “The Tiny Yet Mighty “as” Prop”

Cool pattern! It seems to me that this is basically a specialized form of a render prop. Does that sound right? Using `as` seems to be saying, “Hello component, I want you to render in your normal semantic way based on your name, but I’d like you to use component X where you’d normally use component Y”.

Liked by 1 person

Indeed! It’s very very similar to render prop. One advantage is that it helps with memoization. Often render props are inline functions, which break the memo pattern. Passing a predefined component into `as` preserves it :D.

Liked by 1 person

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s