Introduction

Web components evolve markup into something that's meaningful, maintainable, and highly modular. Thanks to these new API primitives, not only do we have improved ergonomics when building apps, but we gain better overall structure, design, and reusability.

ES6 does the same thing, but for JavaScript. JavaScript is the core language of the web and it's about time it saw a makeover! ES6 brings powerful new primitives to the language, but they're mostly about developer convenience. The result of all the syntactical goodness? More legible, more maintainable, and more modular JavaScript.

The analogy is simple: Web Components : HTML :: ES6 : JS. That is to say, what web components do for HTML, ES6 does for JavaScript.

In this article we'll create a <stock-ticker> custom element using ES6 classes.
First, I'll cover how to create the element using the vanilla web component JS APIs and
then how to create the same element using Polymer.

Defining custom elements from a class

Custom elements can be defined from an ES6 class by extending the HTMLElement DOM interface:

'use strict';
class StockTicker extends HTMLElement {
...
}

Extending HTMLElement creates the element with the correct prototype, inheriting all the methods/properties of the DOM interface.

Note Some browsers require 'use strict'; for using ES6 features but I'll be
leaving it off the other examples on this page.

Element "constructors"

Normally when creating a class, you define a constructor to do initialization work. However,
in the world of custom elements this doesn't apply. Instead, you'll need to use
the createdCallbackcustom element lifecycle method for setup work. Use it to initialize properties, (optionally) create Shadow DOM for the element, set default attributes, etc.

The following example creates a class definition for our stocker ticker element:

Note Spec authors are working to support constructor with custom elements, but
it's currently a hole in the spec. The lack of support is due to the way native
elements are created by the browser.

One nice thing I'm doing here is using a template string to create Shadow DOM from an HTML snippet. Rather than concatenating strings or using escape sequences, we get a nicely formatted multiline string.

Adding properties and methods

Say we wanted to add a symbols property to our element. Users should be able to
configure the list of ticker symbols through the property:

document.querySelector('stock-ticker').symbols = ["GOOG", "GOOGL"];

and also declaratively, using an HTML attribute:

<stock-ticker symbols='["GOOG", "GOOGL"]'>Loading...</stock-ticker>

To add a property like this, you can define a getter/setter that (de)serializes
the JSON when the user sets the property or declares the attribute. This is the type of feature Polymer adds for you. However, if you're working in vanilla custom elements, here's one way to go about it:

There's nothing new here. We're used to registering custom elements by passing
document.registerElement the element name followed by a prototype. The only difference
with classes is that you provide a class rather than a prototype.

Classes are prototypes! Strictly speaking, classes are syntactic sugar for prototypes. You may not see the prototype keyword when creating elements from ES6 classes, but in actuality, a prototype is still being passed under the hood. The JS engine does this for you.

Defining Polymer elements from a class

Defining a Polymer element is similar to defining a vanilla custom element, with
several important differences:

There's no need to use extends HTMLElement. Polymer extends the element for you.

Use the beforeRegister method instead of createdCallback. This is a special method
Polymer calls before creating your element and is where you should define your is property (element's name) and properties object.

If you're using behaviors, define a getter that returns the array: get behaviors() { return [MyBehavior]; }

When registering an element, use the Polymer() constructor instead of document.registerElement.

Building for production

For our ES6 code to work in all modern browsers, we need to transpile it to an ES5 equivalent. BabelJS is my personal favorite. It's the most popular at the moment and comes with
a convenient CLI and Gulp/Grunt/Browserify workflows.

What about ES6 Modules?

ES6 modules are not a native thing to browsers, (yet). It's also unclear how and when they will work with HTML Imports. That's something the spec authors need to figure out. Right now, HTML Imports are a fantastic way to load component dependencies and related HTML/JS/CSS. That's what they've been designed to do! This is the primary reason we use them with Polymer.

That said, if you want to start using a module loader with Polymer today, one option is Imports Module Definition (IMD). IMD is an implementation of the AMD specification that performs absolutely no loading. The primary goal of it is to play nice with HTML Imports, but it should work well with any code loader that doesn't mandate a particular module registry. It's the minimal module system needed if you're already loading your scripts via HTML Imports. HTML Imports handily takes care of loading resources and their transitive dependencies, de-duplicating imports, and executing scripts in the correct order.

Summary

I wholeheartedly welcome ES6 classes to JavaScript! Not only is it fun to write code again,
but when combined with web components, it feels like what web development should
have always been. On one hand, we've got web components pushing HTML forward. On the other, ES6 is pushing JS forward. Together it's a thing of beauty.

Using ES6 with Polymer leads to writing extremely modular, grok'able, and maintainable code.
As you've seen, defining custom elements and/or Polymer elements using classes is also super easy. I like to think of it as writing "componentized JS" that leads to "componentized HTML". It's very satisfying.