๐Ÿงฉ Angular Elements Starter Kit

May 13, 2026 ยท View on GitHub

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.

Angular Angular Elements Tailwind CSS Vitest Yarn


โœจ Features

  • Dual-mode bootstrap โ€” runs as a normal Angular app in development; compiles to a self-contained widget.js in 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.js post-build script produces one clean dist/release/widget.js

๐Ÿ”ง Prerequisites

ToolVersion
Node.jsโ‰ฅ 20
Yarn4 (corepack)
Angular CLI21 (npm i -g @angular/cli)

Enable Yarn 4 via Corepack (once):

corepack enable

๐Ÿš€ Getting Started

# 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 start

Open http://localhost:4200 โ€” you'll see the example chat widget rendered inside the Angular dev shell.


๐Ÿ“œ Available Scripts

ScriptDescription
yarn startStart the dev server at localhost:4200
yarn buildStandard Angular production build to dist/
yarn build:wcBuild the Web Component โ†’ outputs dist/release/widget.js
yarn testRun unit tests with Vitest
yarn watchIncremental dev build in watch mode

๐Ÿ—๏ธ How It Works

Dual-mode bootstrap (src/main.ts)

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 via customElements.define().

Build pipeline (yarn build:wc)

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.


๐Ÿ“ฆ Using the Widget

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.


๐Ÿ› ๏ธ Creating a New Web Component

1. Generate the component

ng generate component components/my-widget

2. Enable Shadow DOM encapsulation

// 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 {}

3. Register it as a Custom Element (src/main.ts)

import { MyWidget } from './app/components/my-widget/my-widget';

createApplication({ providers: [provideZonelessChangeDetection()] })
  .then((appRef) => {
    customElements.define(
      'my-widget',
      createCustomElement(MyWidget, { injector: appRef.injector })
    );
  });

4. Build & ship

yarn build:wc
# โ†’ dist/release/widget.js

Tip: You can register multiple components from the same build by chaining additional customElements.define() calls inside the same .then() block.


๐Ÿ—‚๏ธ Project Structure

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

๐Ÿ”‘ Key Technologies

TechnologyRole
Angular 21Component framework
@angular/elementsCustom Elements bridge
Tailwind CSS v4Utility-first styling
VitestUnit test runner
Yarn 4Package manager

๐Ÿ“š Resources