Create Custom Web Elements using Angular 16 & Integrate with HTML

Ashish Maurya
5 min readSep 16, 2023

--

Introduction

Angular is the popular JavaScript framework for building web applications.

It can be used in various ways like building complete Web Application, creating NPM Libraries, creating Custom Web Elements that can be used in any Non-Angular applications, etc. to name few.

In this Article, we will explore how we can build Custom Web Elements using Angular version 16. And how it can be integrated with simple HTML file, pass values to input properties and listen to the output events.

What is Custom Web Element?

Custom Elements API allows us to create new custom HTML Tags using Web components standards.

This is how Custom Web Element in our case would look like:

<app-custom-card></app-custom-card>

Let’s get Started

  1. Create Angular application by running below command in your terminal,

Note: create-application=false will not generate any application code but it would provide us with initial Angular Workspace folder structure. This is also used to create Monorepo folder structure.

ng new custom-card --create-application=false

2. It would now look like this

Initial Setup

3. Next, let’s generate a Standalone Angular component as below

ng g application my-custom-card --routing=false --style=scss --standalone

4. This will create an application my-custom-card under projects.

Generated my-custom-card application

5. Let’s try to run this generated application using command

ng serve --project=my-custom-card

6. This will build and serve our application on http://localhost:4200/, which would look like

Open in Browser

7. Now, let remove default html code from app.component.html file and add our basic custom card code as below

<div style="text-align: center; border: 1px solid grey; 
border-radius: 5px;width: fit-content;">
<h2>My Custom Card Header</h2>
<br>
<p> My Custom Card Body </p>
<br>
<footer>My Custom Card Footer</footer>
<br >
<input type="button" value="Click Me!">
</div>

8. You would now see how our card would look like

Basic card component

9. Let’s update our app.component.ts & app.component.html file to take these values as Input properties, and provide some default values for now.

import { Component, EventEmitter, Input, Output } from '@angular/core';
import { CommonModule } from '@angular/common';

@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule],
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {

@Input() header: string | null = 'My Custom Card Header';
@Input() body: string | null = 'My Custom Card Body';
@Input() footerContent: string | null = 'My Custom Card Footer';

@Output() cardEvent = new EventEmitter();

title = 'my-custom-card';

handleClick(){
this.cardEvent.emit('Hello from Custom Element!');
}
}
<div style="text-align: center; border: 1px solid grey; border-radius: 5px;width: fit-content;">
<h2>{{header}}</h2>
<br>
<p> {{body}} </p>
<br>
<footer>{{footerContent}}</footer>
<br >
<input type="button" value="Click Me!" (click)="handleClick()" >
</div>

10. Our Output would remain the same as we have provided default values.

Convert Application to Custom Web Element

Now, first we need to install package angular/elements

a. Run below command in the terminal

npm install @angular/elements

b. Open main.ts file, which currently uses bootstrapApplication function to load AppComponent, we need to use new function createApplication, and update the code as below:

import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app.component';
import { createCustomElement } from '@angular/elements';
import { createApplication } from "@angular/platform-browser";

// bootstrapApplication(AppComponent, appConfig)
// .catch((err) => console.error(err));

(async () => {

const app = createApplication(appConfig);

const customCardElement = createCustomElement(AppComponent, {
injector: (await app).injector
});

customElements.define('app-custom-card', customCardElement);

})();

createApplication function create an ApplicationRef, and its injector is passed to createCustomElement Function along with the component that is going to be created which in this case is AppComponent.

customElements defines the element name we want to expose.

c. Now, if you try to build and run the application, you would see blank screen, that is because we are no longer using bootstrapApplication.

Instead we have converted our application to custom element and to make it work we need to update our index.html file to use our new element “app-custom-card” as below:

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>MyCustomCard</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<!-- <app-root></app-root> -->
<app-custom-card></app-custom-card>
</body>
</html>

Refresh the browser! you would now see our custom card output.

d. Let’s see how we can pass input properties and listen to the output event in our index.html file using script tag

<!doctype html>
<html lang="en">

<head>
<meta charset="utf-8">
<title>MyCustomCard</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>

<body>
<!-- <app-root></app-root> -->
<app-custom-card id="my-custom-card-element" header="New Header content" body="New Body Content"
footer-content="New Footer Content"></app-custom-card>
</body>
<script>

const myElement = document.getElementById('my-custom-card-element');
myElement.addEventListener('cardEvent', (event) => {
alert(`Message: ${event.detail}`);
})

</script>

</html>

Note: The way footer-content is updated in index.html, this is how html works!

That’s it! I hope you learned something new and enjoyed along.

--

--

Ashish Maurya

Angular Enthusiast, Web development, Full Stack Development.