Image for post
Image for post

The Case for React-like Web Components

Andrew Rabon
Feb 1, 2018 · 5 min read

I dig React. My favorite feature of React is the emphasis on building small blocks of UI in JSX, and then combining those pieces together to create an app.

Two years ago I wrote about how the choice for Vanilla JavaScript is more compelling than ever before because of new technologies such as Fetch, Promises, template literals, and ECMAScript Modules. That last one is key, because at the time of my post no browser actually supported module loading.

Today, the situation is thankfully much different. Edge 16+, Chrome 61+, and Safari 10.1+ all support ES Modules natively! Firefox is the odd duck out (relevant bug here), but feature detection for ES Modules is really easy (use <script nomodule> for unsupported browsers and <script type=”module”> for supported ones.)

Combined with existing advances such as ES Classes, template literals, and Web Components (specifically Custom Elements and Shadow DOM), ES Modules were the final piece of the puzzle for React-like self-contained UI components.


This post is about my adventures in Vanilla JS and Web Components, and ideas I’ve had while making some small web projects. I may get some things wrong or accidentally endorse an anti-pattern. Feedback and constructive criticism are always helpful!

Creating a React-like Web Component

Take the following React component from the official documentation:

Thanks to JSX, you’re able to easily compose HTML and JavaScript together.

Let’s look at the Vanilla JS version using ES Modules and Web Components:

As you can see, it’s not as elegant and there are some problems (addressed below) which detract from making a direct 1:1, line-for-line comparison. But this is still really powerful for a “bare-metal” JavaScript implementation.

Note: Custom Elements must contain a dash between the first and last characters. This is to differentiate them from the built-in HTML elements such as <input>, etc. It’s good practice to namespace your Web Components (the examples in this post use “fancy-*,” for example.)

Composing with Web Components

Since Web Components are merely fancy HTML elements, it’s easy to use them together to build rich interfaces.

Let’s say we want to make our own, better <button> element:

In Web Components, you can declare <slot> elements which will contain the HTML between your custom element’s opening and closing tags. Let’s see that in action:

In this example, the text “This is my fancy button” will be inserted into our Web Component’s <button> element.

You can also take the programmatic approach:

Composing HTML pages using DOM elements has always been a part of the Web, and Web Components build on that rock-solid foundation.

Note: <slot> does not seem to be supported in Edge yet. 😓

Modularized CSS

As a bonus, you also get something like “CSS Modules” for free due to the Shadow DOM (the variable shadowRoot in the above example.) By default, no styles can penetrate or escape from the Shadow DOM, so there’s no downside to doing something like this:

A caveat is that because styles don’t penetrate, that means in the example above even if the parent page has this:

Our <fancy-button> element will show the default, unstyled <button> element instead. If you want to get that <button> styled from the parent page, you’d have to do something like this:

And then in our Web Component:

However, the inherit property can really only go so far when using it this way.

Unfortunately when using the Shadow DOM, you’re also not able to share CSS Variables across components. That is a bummer. But there is something of a workaround…

Problem A: Styles from the parent page do not cascade into the Shadow DOM

A problem arises when you’re composing a view or a screen and the styles for the page don’t cascade into your Web Component. As mentioned, this is by design. I think in most cases, you should pull as much of each component’s CSS into its own module — it’s the same principle as shying away from global variables.

But, if there are some things you really need to cascade down, the following workaround can get you there:

this.innerHTML bypasses the Shadow Root and exposes all of your component’s HTML to the parent DOM.

I’ve found this pattern is most useful when you’re building your entire app with Web Components and you don’t expect to to share them outside that project. Otherwise, if you intend to open source your Web Components or use them across projects the Shadow DOM method is the way to go.

Problem B: There’s no data-binding

You don’t get data binding for free in Vanilla JS. You can see in my examples that I had to use this.getName() instead of React’s cleaner (which automatically updates with changes.) Proxies might be able to do the job, but I haven’t looked into them much yet. Point: React.

Problem C: You can’t extend native elments like <button> or <input>

You can’t… yet. It’s on the list of things for browsers to do, and I recommend starring or +1'ing the following issues so they know the demand is there:

Chromium: “Launch customized built-in elements”

Web Components: “Support extending elements”

As it stands, my efforts above to “build a better <button>” have involved wrapping one inside of a Web Component instead of extending from HTMLButtonElement, which would be far nicer.

“Should I throw out React/Vue/etc. and start over with Vanilla JS?”

Probably not. React is a robust, battle-tested framework backed by one of the largest Web engineering organizations of today. And even if you’re starting out with a new project, React and other frameworks can still fill gaps that Vanilla JS can not.

However, if your main attraction to React (and Vue, et al) is the ability to compose standalone components with HTML inside, then take a serious look at Web Components. They’re less magic, but are now worth considering for new projects.

If you like this article, please recommend it by clicking the “clapping” icon and by upvoting it on Hacker News and Reddit. 😁

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store