Web Accessibility Essentials
Let’s dive into why web accessibility matters with tips to ensure your site works for everyone.
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.
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.
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):
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.
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.
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.
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.
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';
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>
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' />
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:
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";
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
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.