SCROLL TO EXPLORE • SCROLL TO EXPLORE •
Hero Image

Add an agnostic UI library in Nx monorepo, share across the frameworks

How to share components across different frameworks from a single code base? How to implement Lit (web components) to React & Angular?

Sefa Said Deniz - 4 min read

In my other article, we created an Nx monorepo and added Angular and React applications that we managed to run together. Now it's time to take another step and create an agnostic UI library.

Why are we doing this?

We are learning how multiple teams in big enterprises work together. Where multiple structures are in place and teams have to manage all these projects. Although the frameworks betweens may differ, they have something in common. And that's the UI, because the UX should be consistent. It can't be that in one application the primary button is blue and in another it's green.

Add UI library to nx

To achieve this, you have to share the UI library across the frameworks. And it should be an agnostic UI library, otherwise you can't easily share an Angular component directly with React applications (or vice versa). And don't forget, it should also be developer friendly. Since we have the Nx monorepo, we can create a library like that. After we are finished, it will be like this (you can show this graph with the nx graph command):

Add UI library to nx

Choosing the Library

When I did some research I discovered two potentially interesting options for UI libraries. Both looking good:

Both have their pros and cons. Let's dive in.

StencilJS

Initially, StencilJS seems perfect for our use case. It has automatic generators and wraps around the frameworks. It looks amazing because this way every developer can work with the UI library natively. Unfortunately when I try to implement it into the Nx monorepo I encounter big challenges due the bugs in nxext/stencil package. On top of that, it's not really compatible with Angular 17 and React 18. Maybe in the future we can try again.

Stencil bug

Lit

From my past experiences, I already knew the Lit and web components. And I know that we can use Lit in our applications when it's compiled to web components. But still, I did my homework and did additional research on Lit.

Lit

What is Lit

Initially, Lit is a simple library for building fast, lightweight web components. So when you create a component with Lit, it builds into a web component. Then you can use this web component in Angular, React, Vue applcations and even directly in HTML pages. So it looks perfect for us.

Installing Lit to the Nx Monorepo

First, we have to install the Nx/JS library (if it's not already installed):

npm i -D @nx/js

We are installing dev dependencies because we will only use it to generate libraries and not use it in the runtime.

Then, create a library in the Nx monorepo (of course you can choose the directory):

nx generate @nx/js:library --directory=libs/ui

For personal preference and I think better management, I want to install my UI library under the libs folder. Now we have a shared library.

Installing Lit:

npm i lit --save

To better understanding we will create a simple LitElement. Just create a .ts file under libs/ul/src/lib/yourcomponentname/yourcomponentname.ts (you can choose the folder structure). And create a simple button with LitElement:

import { LitElement, css, html } from 'lit'; import { customElement, property } from 'lit/decorators.js'; @customElement('simple-greeting') export class SimpleGreeting extends LitElement { static override styles = css` button { background-color: blue; color:white; } `; @property() name?: string = 'World'; override render() { return html`<button>Hello, ${this.name}!</button>`; } }

Now we have to export the component. To do that just write a single line into ui/src/index.ts:

export * from './lib/components/simple-greeting';

Using LitElement Component in an Angular App

Now the fun part begins. We just created our first component and we need to use it in our applications. Let's start with our Angular application. In order to use web components in Angular applications, you need to add CUSTOM_ELEMENTS_SCHEMA into your component.

schemas: [CUSTOM_ELEMENTS_SCHEMA];

Now we are importing our library with

import 'libs/ui/src/index';

After importing we can use our component with:

<simple-greeting name='Earth'></simple-greeting>

Add UI library to angular

Using Lit Element Component in React App

For React applications unfortunately we can't directly use them. So we need to wrap the component using @lit/react. First we need to install it:

npm i @lit/react --save

Then we need to update the component:

import { createComponent } from '@lit/react'; import * as React from 'react'; // Existing component code... export const SimpleGreetingComponent = createComponent({ tagName: 'simple-greeting', elementClass: SimpleGreeting, react: React });

Now we can use our component in the React application. To do that you need to import it and use it like a React component:

import { SimpleGreetingComponent } from "ui/src/index"; <SimpleGreetingComponent name='Earth' />

Add UI library to react

Handling Errors

When I implement my component to the React application I encounter some difficulties. Maybe you will have the same difficulties as well. So here are my solutions:

Reflect Metadata error

Add UI library to react error

If you encounter this error, just install the reflect-metadata package to you dev dependencies:

npm i reflect-metadata --save-dev

Then import to your web component:

import "reflect-metadata";

For property errors

Add UI library to react error

If you have this error, you can change your properties like

static override properties = { name: { type: String } } name = 'World';

That will solve the problem. More details here

Conclusion

Now we have two applications using the same UI library from a single code base. We can extend this library and add other components like cards, inputs ect.

"Adding UI library celebration"

logo