A new, lean way of creating web apps

Kenneth Christiansen
6 min readSep 24, 2018

--

As a browser engineer and a spec editor, I am always quite skeptical of frameworks and libraries — some are often bigger than I would like and can make it harder than needed to accomplish goals like a very low time-to-interactive.

If we as browser engineers do our job right, well, then you should be able to get pretty far without depending on a framework.

We are not there yet, but technologies like Web Components and the proposed Template Instantiation, we are getting closer and closer.

I have long liked the React way of constructing applications, by props/data down, events up — it makes it much easier for me to reason about than two-way bindings and the like. JSX also makes it easy to write declarative HTML and mix it with the JavaScript you know and love, when needed, instead of some custom declarative template language, that looks like something you know, but then really isn’t.

Fight fire with fire they say, I say just use lit-html ;-)

Now I love the rapid application development where you code and directly see the result in the browser without first having to build the project and wait for it to complete. With ES module and now the lit-html project, I can do this, for the cost of a few compressed kilobytes (will be even less then Shady CSS can be optional)! You can also build everything up using Polymer CLI and do code splitting using fragments (though I ran into a few issues when attempting that, and haven’t had time to dig deeper)

If you want to know a bit more about lit-html, check out my earlier story:

Lit-html doesn’t come with a component model by itself, but it can easily be used with Web Components, but you need to call render() yourself and handle attribute and property change, for instance if these should result in a render() call.

For this reason, I created a base class called LitElement a while back and experimented a bit with how such a class could look. I also shared some of my ideas and feedback with Justin Fagnani, who is the creator of lit-html. Since then the Polymer team have created their own flavor, depending on the property handling from Polymer.

During the last month or so, there has been an effort on going to improve the API and loose the dependency on the old property handling code from Polymer (yay 🎉). This makes it a lot more aligned to my approach — which I applause — and for this reason, I have spent quite some time giving feedback and help improve the documentation for the new API, and I must say that I really like the result!

A small example on how you could create a lazily loaded image element using LitElement

Check out the docs here:

With the new API ready, the Polymer team restarted their effort of creating native Material Design web components based on the official material components from Google Developers

Currently only a few of the components are available, but what it there works really well:

Some material web components in action

You can play around with them yourself right here.

Trying it all out!

With all that ready, I spent a few days converting some of my teams front end applications over to LitElement and the new Material Web Components. As not everything is ready yet and the existing apps are written in Polymer 2.0, I had to move a few parts to Polymer 3.0 as well.

LitElement, Material Web Components and traditional Polymer elements works great together

The experience was quite pleasant — all paper-, iron- and app-elements from Polymer works great with ES modules and can easily be used together with LitElement

Mixing LitElement with Polymer’s app-elements

The result is quite nice given how little time I spent on this and I find the code much easier to read and follow.

Our W3C Generic Sensor API manual tests app now runs smoothly with LitElement

You can check out the result here, and feel free to report issues and submit patches :-)

Bonus: idle load until visible

While porting the application, I noticed that it was eagerly fetching most resources and building up the whole user interface, including hidden parts.

I applied some IntersectionObserver magic to only load images when visible, which also helps to save on bandwidth as the images are currently gifs (urgh!).

The actual tests are being build up by the <sensor-test-page> web component which loads a JSON file and creates test and user interface from that. Loading all the test at the get-go is a bit foolish and negatively affects time-to-interactive, so loading these when the view is actually visible (lazy loading or similar) makes sense.

I could easily have hooked up that logic to the router (which the old version did) but again, using the IntersectionObserver was much cleaner as the component could take care of it itself.

But of course, lazy loading that way means that we will also load content when switching views, which negatively affects the user experience. It is quite fine that the images are loading lazily and appear as they arrive as long as they don’t cause element resizes in the view, but it would be nice to have the views ready.

The solution is of course to use another feature only supported in Chrome and Firefox, called requestIdleCallback . Check out the MDN docs here.

The idea is doing work when idle, ie. when the user isn’t interacting with the page or the page is doing other kinds of work. Basically do the work as soon as possible without getting in the way. This can of course be combined with the IntersectionObserver to make sure that the view is created as soon as it is needed — basically an idle load until visible approach.

Let’s call our function fn when idling or immediately if target becomes visible

This works pretty well and ensures that the visible view is always loaded first (e.g. if you load the app with a deep-link to a specific view like domain.com/deep-link)

Using it is super simple. In our app we fetch the JSON file this way and fill in our this.items property which then is re-rendered by lit-html. It is almost magic!

--

--