Building Custom Elements / Web Components with Angular 6

5-step guide using Angular CLI and Angular Elements

With the newest Angular CLI (version 6, released 2018–04–03) and the new addition to Angular family — the Angular Elements package it’s extremely easy to create native custom elements.

If you don’t know what custom elements are or what is the connection to Angular, there are several great talks that introduce to these concepts, I highly encourage you to watch one of these.

So now, without further ado, let’s see some code!

1. Install Angular CLI 6 and initialize the project

npm i -g @angular/cli
ng new
elements-demo --prefix custom

We’re not doing anything special yet, so all the normal ng new parameters apply, you could add sass or anything to the mix, but here we’ll stop at setting a custom prefix to, well, “custom”.

2. Add elements & polyfill

In order to have elements functionality available we need the Angular library and a polyfill — with the new CLI it’s just a matter of one(!) simple command:

ng add @angular/elements
Just type a command and a machine is doing all the work — isn’t this awesome?!

3. Create a component

Let’s create one with Input and Output to see how they translate to custom elements that are understood by browsers:

ng g component button --inline-style --inline-template -v Native

We use ViewEncapsulation.Native so that the styles are bundled with the template and the component’s class into one file.

After adding some style & template our button.component.ts looks like this:

You can copy this code from:

4. Registering component in NgModule

This is the vital part: we use the Angular’s createCustomElement function to create a class that can be used with browsers’ native customElements.define functionality.

Angular documentation describes this best:

createCustomElement Builds a class that encapsulates the functionality of the provided component and uses the configuration information to provide more context to the class. Takes the component factory’s inputs and outputs to convert them to the proper custom element API and add hooks to input changes.

The configuration’s injector is the initial injector set on the class, and used by default for each created instance.This behavior can be overridden with the static property to affect all newly created instances, or as a constructor argument for one-off creations.

What is also special about this module is that, since our ButtonComponent is not a part of any other component, and is also not a root of an Angular application, we need to specifically tell Angular to compile it: for this we put it on the entryComponent list.

We also need to tell Angular to use this module for bootstrapping, hence the ngDoBootstrap method.

Our app.module.ts should now look like this:

5. Build, optimize and run the code

To try our component out we will serve a simple html with http-server, so let’s add it:

npm i -D http-server

In order to build we will use a standard ng build command, but since it outputs 4 files (runtime.js , scripts.js, polyfills.js and main.js) and we’d like to distribute our component as a single js file, we need to turn hashing file names off to know what are the names of files to manually concatenate in a moment. Let’s modify the “build” script inpackage.json and add “package” and “serve” entries:

"build": "ng build --prod --output-hashing=none",
"cat dist/elements-demo/{runtime,polyfills,scripts,main}.js
| gzip > elements.js.gz",
"serve": "http-server --gzip"

Now the sample [projectFolder]/index.html :


And let’s see this in action!

npm run build && npm run package
npm run serve


So what were the crucial things we did here? In summary we’ve:

  • added elements-relared libraries with ng add command
  • registered the Angular component as custom element in a module
  • combined build artifacts into one file and gzipped them

Not that difficult, right? Personally, I am extremely excited that this is such an easy process — it will definitely encourage Angular devs that might have been a conservative in looking into custom elements, to just start playing with the tech, and soon, use it as a standard tool in their tool belts.

Oh, and btw. the resulting elements.gz.js weights 62kB, which, considering that we have the full power of Angular framework inside it, is a pretty remarkable result!

As usual, you can browse the completed code on Github.

Did you learn something new? If so please:

→ clap 👏 button below️ so more people can see this
follow me on Twitter (@sulco) so you won’t miss future posts!

Frontend trainer, Angular enthusiast, family man,