Build web components out of an existing Angular app with Angular elements

Mick Lawitzke
4 min readMar 3, 2021

--

Maybe you have built an awesome website or web-app and another team of your company builds an associated app for smartphones with a different web-framework and wants to reuse some of your existing components.

Building/Extracting your components as web components makes them reusable in each and every other web project.

The idea

For each component we want to extract as a web component we need a separate Angular app. From that app we can import everything we want to have in our web component from our original app: Modules, services, animations, styles. Import everything you want or even create a tailored composition only for the web component, no limitations!

Luckily Angular CLI comes in handy and provides us with everything we need to have multiple apps in our project.

In addition, for convenient building and publishing our web components to npm, we can use Lerna.

Project setup

Let’s assume you have your existing Angular app and it is awesome, of course. Great! Start by adding a new app into your project using Angular CLI which will house our first web component:

ng generate application myWebComponent --routing=false --skipInstall=true

Replace myWebComponent with the name of your choice. We set routing=false because a web component does not have routing and the skipInstall=true prevents rerunning npm/yarn install.

Within your project Angular CLI now creates a new projects folder with a new app in it. It also sets everything up in angular.json so it is ready to use!

Don’t worry about your initial app: You can still use all your common commands like npm start or npm build as before. This is because Angular CLI has defined your base app as the default project in angular.json:

"defaultProject": "myAwesomeApp"

Cleanup and setup

Inside our newly generated ./projects/my-web-component folder remove the created favicon and environments folder. If you want to just extract an existing component you can delete all app.component stuff. If you want to tailor something customized together, only for the web component, keep the app component and use it as your base component.

For convenience add a path-variable to the web component app’s tsconfig.app.json compilerOptionsthat references our base app:

"compilerOptions": {
...
"paths": {
"@app": [
"src"
],
"@app/*": [
"src/*"
]
}
},

Don’t forget to update the environment import in main.ts:

import { environment } from '@app/environments/environment';

Additionally, if you want, rename the folder ./projects to something more descriptive like ./web-components but don’t forget to update all occurrences within angular.json , too. Within angular.json you can also set "newProjectRoot": "web-components", so every other new app will land there, too.

Creating web components with Angular elements

Add Angular elements to your project. By using Angular CLI’s add command and providing a project it even adds the required polyfill only to the web component app and not our base app:

ng add @angular/elements --project=myWebComponent

When you add more web component apps dont forget to add the polyfill document-register-element to them, too.

Thats it! Now you can import what you want from your base app in your web component app and bootstrap them with Angular elements as usual:

import { BrowserModule } from '@angular/platform-browser';
import { DoBootstrap, Injector, NgModule } from '@angular/core';
import { createCustomElement } from '@angular/elements';
import { SomeComponent } from '@app/src/some.component';
@NgModule({
declarations: [
SomeComponent,
],
imports: [
BrowserModule
],
providers: [],
entryComponents: [
SomeComponent,
],
})
export class AppModule implements DoBootstrap {
constructor(private injector: Injector) {
const webComponent = createCustomElement(SomeComponent, {injector});
customElements.define('my-web-component', webComponent);
}
ngDoBootstrap(): void {
}
}

If you want to add global styles, that are not part of any component, import them relative in your styles.scss. You can import everything all together or be selective:

@import "../../../src/styles";

For assets you want to include edit angular.json and change the assets array for your Web Component app. You may remove the favicon that we deleted there.

Build

Build the web component from the root of your project with the app name, in this example ng build myWebComponentor ng build myWebComponent --prod

Preview / watch

If you edit the web component app’s index.html and replace <app-root></app-root>with your defined tag name in app.module.ts you can even use ng start myWebComponent to see the web component in action and try it out. You may also delete the favicon from index.html.

Publish to npm

For publishing to npm add a package.json and a README.md to each web component app’s folder and copy them in your dist folder after build. You could do that, for example, within a npm post build script.

Build, version and publish multiple web components automatically with Lerna

If you have a lot of web components it may become unhandy to do ng buildand npm publish for each of them manually. Luckily there is a tool called Lerna that does that for us. This may be a topic for itself, there is an in-depth article you can read about it. But in a nutshell:

  • Install Lerna
  • Define web-components/* as your packages within lerna.json.
  • Within angular.json change your web component app’s outputPath to be within the web component app’s root folder.
  • Add a build script to each web component app’s package.json.
  • Add a prepublish script to each web component app’s package.json that builds the app and copies package.json, e.g. rm -rf dist && yarn build myWebComponent --prod && cpx \"{package.json, README.md}\" dist. Lerna will execute this for each app before publishing.
  • Now you can run lerna publish and it will automatically build all web component apps, increase their version number in package.json, publish them to npm and create git tags.

--

--