Build web components out of an existing Angular app with Angular elements
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
compilerOptions
that 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 myWebComponent
or 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 build
and 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 withinlerna.json
. - Within
angular.json
change your web component app’soutputPath
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.