A modern, opinionated starter kit for building Custom Web Components (Web Components / Custom Elements) powered by Angular 21, Tailwind CSS v4, and Vitest — ready to drop into any web page or framework.
- Dual-mode bootstrap — runs as a normal Angular app in development; compiles to a self-contained
widget.jsin production @angular/elements— wraps Angular components as standard Custom Elements (<my-custom-widget>)- Shadow DOM encapsulation — components are style-isolated by default via
ViewEncapsulation.ShadowDom - Zoneless — uses
provideZonelessChangeDetection()for maximum performance and smaller bundles - Tailwind CSS v4 — utility-first styling included out of the box
- Vitest — fast unit testing via the Angular CLI Vitest builder
- Single-file output —
concat.jspost-build script produces one cleandist/release/widget.js
| Tool | Version |
|---|---|
| Node.js | ≥ 20 |
| Yarn | 4 (corepack) |
| Angular CLI | 21 (npm i -g @angular/cli) |
Enable Yarn 4 via Corepack (once):
corepack enable# 1. Clone the repo
git clone https://github.com/your-org/elements-template.git
cd elements-template
# 2. Install dependencies
yarn install
# 3. Start the dev preview
yarn startOpen http://localhost:4200 — you'll see the example chat widget rendered inside the Angular dev shell.
| Script | Description |
|---|---|
yarn start |
Start the dev server at localhost:4200 |
yarn build |
Standard Angular production build to dist/ |
yarn build:wc |
Build the Web Component → outputs dist/release/widget.js |
yarn test |
Run unit tests with Vitest |
yarn watch |
Incremental dev build in watch mode |
The entry point detects the environment flag and switches modes:
development → bootstrapApplication(App) full Angular dev shell
production → createApplication() +
createCustomElement(Example) registers <my-custom-widget>
- Dev mode boots the standard Angular app so you can iterate fast with HMR.
- Production mode calls
createApplication()(no root component) and registers each Angular component as a native Custom Element viacustomElements.define().
ng build --configuration production
└─ dist/elements-template/browser/main-<hash>.js
↓ concat.js
dist/release/widget.js ← single deployable file
concat.js locates the hashed main bundle and copies it to a stable, hash-free filename you can reference from any HTML page.
After running yarn build:wc, include the single output file in any HTML page — no Angular, no build tools needed on the consumer side:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>My Page</title>
</head>
<body>
<!-- Drop the custom element anywhere on the page -->
<my-custom-widget></my-custom-widget>
<!-- Load the self-contained bundle -->
<script src="path/to/widget.js"></script>
</body>
</html>The widget is fully encapsulated — its styles live inside the Shadow DOM and will not bleed into or be affected by the host page's CSS.
ng generate component components/my-widget// src/app/components/my-widget/my-widget.ts
import { Component, ViewEncapsulation } from '@angular/core';
@Component({
selector: 'app-my-widget',
templateUrl: './my-widget.html',
styleUrl: './my-widget.css',
encapsulation: ViewEncapsulation.ShadowDom, // ← required
})
export class MyWidget {}import { MyWidget } from './app/components/my-widget/my-widget';
createApplication({ providers: [provideZonelessChangeDetection()] })
.then((appRef) => {
customElements.define(
'my-widget',
createCustomElement(MyWidget, { injector: appRef.injector })
);
});yarn build:wc
# → dist/release/widget.jsTip: You can register multiple components from the same build by chaining additional
customElements.define()calls inside the same.then()block.
src/
├── main.ts # Dual-mode bootstrap
├── environments/
│ ├── environment.ts # production: true → Web Component mode
│ └── environment.development.ts # production: false → Dev shell mode
└── app/
├── app.ts / app.html # Dev-only shell (not part of the WC build)
└── components/
└── example/ # Sample chat widget component
├── example.ts # ViewEncapsulation.ShadowDom
├── example.html
└── example.css
concat.js # Post-build script → dist/release/widget.js
| Technology | Role |
|---|---|
| Angular 21 | Component framework |
| @angular/elements | Custom Elements bridge |
| Tailwind CSS v4 | Utility-first styling |
| Vitest | Unit test runner |
| Yarn 4 | Package manager |