Framework agnostic component libraries with StencilJs and Nx

StencilJS WebComponents 12-08-2022

Why Stencil and WebComponents?

Web components are a collection of web standards that allow to create modular and reusable HTML elements. Common features of large frameworks like component lifecycles and dynamic are natively supported. Since they are standards-based only, you don’t need a framework to run them; a modern browser is enough.

Web components primarily consist of the following 4 APIs from the web standards:

Custom Elements

The Custom Elemets standard gives us the possibility to define our own HTML elements.

Shadow DOM

The Shadow DOM allows us to encapsulate stylesheets and markup from the rest of the markup so that they no longer affect each other.

HTML Templates

With HTML Templates you can define reusable snippets. Within an HTML template, no scripts are executed, stylesheets are applied, or images are loaded. As long as it is not applied, it is not a regular part of the current document.

ES Modules

The ES Modules specification defines a standard way to import and reuse Javascript files in the most performant way.

Stencil building blocks

Stencil is a web component compiler developed by the team around the Ionicframework. Stencil is not the next framework but a compiler whose output format is standard-compliant web components. There are a few other compilers and frameworks that do this. There is the possibility to work without such a compiler, but this is quite bulky in handling and makes little sense, especially for a larger codebase.

import { Component, Prop, h } from '@stencil/core';

	tag: 'my-component',
	styleUrl: 'my-component.css',
	shadow: true,
export class MyComponent {

	@Prop() name: string;

	render() {
	    return <div>Hello, World! I'm ${}</div>;

Depending on personal experience with different frontend frameworks, at first glance, a Stencil component looks something like a mix of Angular and React. Stencil uses TSX (JSX in Typescript) for templating and Typescript as the leading language for development. Common stylesheet preprocessors like Sass are supported as well as Less and PostCSS.

Hands-on with Nx

My colleague Robert Weyres here has already written an article about Nx and Angular libraries.

For a small example, we now create an empty Nx monorepo:

npx create-nx-workspace --preset=empty

And install the plugins that are important for us:

npm install --save-dev @nrwl/angular @nxext/stencil

With the Stencil plugin we now create a new library.

npx nx g @nxext/stencil:lib core --style='css' --buildable

Framework Integration

Stencil has support to generate extra wrappers for frameworks like Angular, React, Vue and Svelte. Although most of these frameworks can handle web components, they still feel like foreign bodies in the application. I’m using Angular as an example in the examples here.

Using the Stencil Nx plugin, you can generate the outputconfig with the corresponding Angular library.

npx nx g @nxext/stencil:add-outputtarget core --outputType='angular'

This adds the following to stencil.config.ts:

	componentCorePackage: '@agnostic-library/core',
	directivesProxyFile: '../../../libs/core-angular/src/generated/directives/proxies.ts',
	valueAccessorConfigs: angularValueAccessorBindings,

After this, we created the following folder structure within the “libs” directory:

|       |-core-angular

With each build of the “core” library, wrapper components are now generated with Angular for each stencil component in the “core-angular” library folder. This is an Angular library without its own components, which only contains an Angular module for the export and the generated wrappers.

So that the new Angular library now also makes the components usable, these must be exported. For this, the new Angular module is extended and the new component is made known and usable in applications:

import { MyComponent } from '../generated/directives/proxies';

const components = [

    imports: [CommonModule],
    declarations: components,
    exports: components
export class CoreAngularModule {}

An Angular application can now import and use the library, but the Web Components must be made known to the browser for everything to work. Before that, we face the problem that the components are not rendered in the browser. For this, we enter the following into the main.ts of the Angular application:

import { defineCustomElements } from '@agnostic-library/core/loader';


The generated Angular components use the original Web Components and make them feel like native Angular components.

This in itself does not sound very spectacular at first. I have observed over the years, especially in large companies, that the frameworks used for the frontend are changed from time to time, or several are used in parallel by different teams. If you now create component libraries via standards such as Web Components, which correspond to the corporate design, for example, these can be adapted quite quickly for all frameworks. This allows efforts to be bundled because a “build once, use everywhere” approach can be used. Not every component has to be rewritten in every framework, but they can be shared. As a small bonus and since Web Components only need a browser as an environment, they can also be used without a framework, e.g., on the company website or landing pages.

On Github is a small example and in the Nxext Demo a somewhat larger repository with more extensive components, an Angular application with backend.

Author's photo

Dominik Pieper

I'm the Topic Lead Frontend and a Solution Architect at Conciso GmbH. 🅰️ Frontend Architect at Conciso GmbH | 🌊 Nx Champion | 💡 Nxext plugin author

See other articles:


Nx plugins getting started

An Nx plugin is an npm package to extend Nx itself. The most significant parts of that are generators and executors. Generators are the way to create files from templates, and executors are runners for something like the build process to package your application. It’s easy to get started.

Nx Vite TypeScript Plugins 02-12-2022