Web

Why Web Components Aren’t Ready for Production… Yet

by TJ VanToll on September 02, 2014 | 5 Key Forces Shaping Next Generation of Enterprise Mobility


Web components are the new hotness. And now that a complete web components implementation landed in Chrome 36, we finally have stable, unprefixed, unflagged version to try out. But, although web components are certainly something to be excited about, and a technology worth experimenting with, that doesn’t mean that they’re ready to use in your production applications — because for most applications, they’re not.

In this article you’ll see why that is. We’ll discuss the current issues with using web components in production, and what needs to be done to solve them.

Browser support

The obvious reason to avoid web components is browser support. Although web components landed in Chrome 36, they only have partial support in Firefox, and they are not present in Safari or IE. Because cross-browser support won’t be possible for a very long time, if it happens at all, a polyfill is a long-term necessity for developers that want to use web components outside of Chrome.

And there is seemingly good news on this front: the Polymer team maintains a complete set of polyfills collectively known as “The Platform”. But although Polymer’s official documentation makes it seem like getting cross-browser web components support is as easy as including platform.js, things aren’t quite that simple.

All polyfills aren’t created equal

Although developers tend to think of a polyfill as a polyfill, the complexity of the implementations can vary widely depending on the technology being polyfilled. APIs that are syntactic sugar for existing APIs tend to be the easiest to implement. For instance you can write a Function.prototype.bind() polyfill in a handful of lines of code, and a classList polyfill polyfill in about 70 lines of code.

APIs that add entirely new behavior, or that include CSS syntax changes, tend to be harder to write. For example consider a pointer events polyfill. Because pointer events work with the touch-action CSS property, and because browsers ignore CSS rules for properties they do not understand, pointer event polyfills have to get creative. Polymer’s pointer events polyfill requires you to use a touch-action attribute on elements — e.g. <div touch-action="none"></div> — and forego the CSS declaration entirely. Microsoft’s HandJS polyfill does a text-based search of all <style> and <link rel="stylesheet"> tags for the touch-action property, and then replicates that functionality in JavaScript — which obviously has non-trivial performance implications.

The point here being, some technologies are harder to polyfill than others. Why is this relevant? Well, as it turns out, polyfilling web components is the king of all polyfilling challenges. Simply put, writing a web components polyfill is absurdly difficult, and the inherit complexity has non-trivial implications on the viability of using web components polyfills in production. Let’s look at some examples to demonstrate this.

The insanity that is polyfilling web components

To start, consider the encapsulation that the shadow DOM specification allows. When HTML elements live within a shadow root they are hidden from functions such as querySelector() and querySelectorAll(). For example, in the following screenshot querySelectorAll() does not find the <h1> element because it’s within a shadow root:

687474703a2f2f692e696d6775722e636f6d2f5942615758545a2e706e67

Think for a moment how you would polyfill this behavior. Crazy, right? But, if you add Polymer’s platform.js to the same example, it unbelievably works. That is, querySelectorAll() does not find elements within a shadow root — in browsers that have no concept of a shadow root. The following screenshot shows this behavior in Safari:

687474703a2f2f692e696d6775722e636f6d2f657879325341542e706e67

I had to run this a few times to verify my own sanity, as I had no idea how this was even possible. How does a polyfill do this?

The answer is, Polymer’s shadow DOM polyfill wraps a large number of DOM methods — at least 25 of them — with a series of customized shims that exclude elements that reside within its internal list of shadow roots, and it does this for over 30 HTML element interfaces. No, I’m not kidding.

And we’re just getting started with the crazy polyfilling challenges that web components present. The shadow DOM specification also defines a number of new CSS concepts for exposing and selecting elements that reside within shadow roots. So, because browsers discard CSS selectors and rules they don’t understand, this means that polyfills must resort to text-based searches of <style> and <link rel="stylesheet"> tags. This 38-line block of regular expressions defined in platform.js’ source should give you an idea of the difficulty of doing this:

687474703a2f2f692e696d6775722e636f6d2f473659467072742e706e67

Why this stuff matters?

I want to make it clear that the code examples I show above are not meant to be a reflection on the engineering effort put forth by Google, but rather an attempt to show the complexity inherent to reimplementing web components in JavaScript. The web components specification aims to give hooks into browser internals, so it makes sense that it’s nearly impossible to replicate that functionality in JavaScript. Given this, the job the Google did with the platform polyfills is extraordinary.

But, given that Google has already done the hard work, why should you care about how difficult it was? Because the complexity has several adverse effects on the feasibility of using these polyfills in production. Let’s look at each of these issues in turn.

Not everything is supported

The first issue is, even with some of the code examples shown above, Polymer’s polyfills do not support all the functionality that web components offer (most notably shadow DOM), or as the source says:

“The intention here is to support only the styling features which can be relatively simply implemented. The goal is to allow users to avoid the most obvious pitfalls and do so without compromising performance significantly.”

Although I disagree with Google’s interpretation of “relatively simply”, I understand the sentiment here, as truly polyfilling all of shadow DOM would require rewriting CSS in JavaScript, and even Google agrees that goes too far.

The problem is, from a developer’s perspective it’s hard to know what will work and what won’t. The only documentation of this I can find is hidden, such as this 113-line comment in a source file discussing which portions of shadow DOM’s CSS is supported.

Remember the example above of querySelectorAll() not finding an <h1>? That same example has different behavior when styling that <h1> in a <style> tag. The following shows the difference in Chrome and Safari:

687474703a2f2f692e696d6775722e636f6d2f63514d57706b662e706e67

The polyfills are also big

The next issue is sheer file size. As of version 0.3.3, the platform polyfills comprise 151K of JavaScript, 44K gzipped. If you choose to use Polymer on top of the polyfills, you can add another 66K, 20K gzipped. For comparison, people complain incessantly about the size of jQuery, which is 29K gzipped.

Polymer does do a good job of separating the polyfills into separate modules so you can pick and choose the ones you need, but in practice almost no one actually does that (the one exception being Mozilla’s X-Tags library). All of Polymer’s elements, and most (all?) of the elements listed on http://customelements.io/ and http://component.kitchen/ depend on Polymer, which depends on the platform in its entirety.

Performance considerations

Beyond the overhead of shipping the polyfills across the network, there’s also the time it takes for the browser to parse and interpret the JavaScript code, which is a known performance bottleneck on mobile devices. Polymer’s documentation states that they do not yet do performance benchmarking, which is unfortunate considering that many of the things Polymer’s polyfills do are inherently slow.

One performance issue worth specifically discussing is the numerous requests generated by HTML imports. The idea behind HTML imports is great: a single .html file that contains everything you need — templates, CSS, JavaScript, other HTML, and so forth. As such, it’s relatively common to see an HTML import that depends on several other resources, each of which must be resolved over HTTP.

In browsers that support HTML imports natively — i.e. in Chrome — the browser can resolve these resources using parallel connections, usually ~6–8 per hostname, as well as leverage speculative parsing algorithms to optimize the loading of these resources. This works great in Chrome. But in all other browsers, when HTML imports aren’t natively supported, polyfills must resort to queued up XHRs, which can be painfully slow.

This isn’t very noticeable on a demo of one web component on a high-end development machine, but it shows on pages with multiple components — especially on slow networks and mobile devices. The best example of this is actually Polymer’s site itself, which uses Polymer to build everything you see. Visit http://www.polymer-project.org/ in any browser that isn’t Chrome 36+ and see the performance issues for yourselves (iOS Safari and IE Mobile are particularly bad).

Now, there is some good news on this front, as some tooling is emerging to aide with the performance issues. Vulcanize is a build tool from the Polymer team specifically for HTML imports. In a nutshell, you pass Vulcanize an HTML file and it outputs that HTML file with all HTML imports inlined, including deep dependencies. For example, the following inlines all imports in an index.html file, and places the output in a built.html file:

$ vulcanize -o built.html index.html 

That being said, build tools such as Vulcanize are still in their infancy. A lot more research needs to be done to show how to incorporate a tool such as this in to existing development processes and workflows.

So what does this all mean?

Web components and Polymer are exciting technologies that may fundamentally change the way we develop web applications, but because of the large performance gap between browsers that support the technologies natively (aka Chrome 36+) and those that don’t (aka every other browser), it will be difficult for most developers to use web components until they’re implemented everywhere, and there’s no way of knowing how long that will be.

Here are a few things I think would help the adoption of web components:

  • Browser support—Duh. But seriously, web components without any polyfills perform great in Chrome 36+; it’s the polyfilling that causes the issues.
  • Better modularity—99% of published web components rely on Polymer, which relies on platform.js, which means always downloading 66K of gzipped JavaScript. jQuery doesn’t get a free pass for 29K, so Polymer shouldn’t get one for 66K (mental note: register youmightnotneedpolymer.com). Polymer could be better about micromanaging its dependencies, and documenting how to do that in third-party components. Shadow DOM is easily the biggest and most complex web components polyfill, and many components don’t even need it — yet, 99% of them are downloading and parsing it.
  • Performance benchmarks—If the Polymer team expects people to use web components in real apps, then they should start benchmarking the polyfills in the browsers developers need to support, especially mobile ones.

Bear in mind that none of this is especially surprising given that Polymer is still officially designated as a “developer preview”. I’m confident that better tools, better modularity, and better performance will come in time, but Polymer and web components are still really new in the web development world.

How This Affects Kendo UI

At Telerik we get a lot of questions about when Kendo UI will support web components, as UI widgets are seen as an ideal use case for the web components model. But, although we certainly look forward to the day we can provide <kendo-calendar> as an API, we have to weigh that against the need to provide high quality and performant components for our users in the browsers we support.

We’re continuously researching Polymer and the web components specification so that we can leverage the benefits they provide when appropriate, and the research presented above is a direct result of that. Simply put, Kendo UI does not support web components today because we cannot provide high quality and performant components using them.

We are, however, concentrating on making our widgets as easy as possible to use with production-ready technologies; therefore, although you can’t write <kendo-calendar> yet, you can write <div data-role="calendar"> using our declarative data bindings, as well as <div kendo-calendar="..." k-ng-model="..."> using our comprehensive set of Angular directives. We’re also constantly listening to what our users want. If you’re interested in seeing web components and/or Polymer in Kendo UI, let us know in our feedback portal.

What’s next

This article purposely focused on the feasibility of using web components in production today, and did not touch on whether web components are actually worth using — that discussion is coming in a future article.


TJ VanToll

Developer Advocate for Telerik

is a developer advocate for Telerik, a jQuery team member, and the author of jQuery UI in Action. TJ has over a decade of web development experience—specializing in performance and the mobile web—and speaks about his research at conferences around the world. TJ is @tjvantoll on Twitter and tjvantoll on GitHub.

More from TJ VanToll
All articles

Follow us