Custom web elements for Angular and React with Nx + xplat

Nathan Walker

Nathan Walker

Posted Mar 16, Updated Apr 1

elements

Let's look a new generator to create custom elements from any Angular components in your workspace to use in another framework like React.

Whether you work in a large enterprise among diverse development teams using different technology stacks or just a startup wanting to make the right decision on how to build out your company's technology from square one, Nx provides peace of mind.

# Custom web elements for Angular and React with Nx + xplat The [Nrwl](https://nrwl.io/) team [recently introduced the ability](https://blog.nrwl.io/building-angular-and-react-applications-together-with-nx-78b5578de598) to generate a [React](https://reactjs.org/) app inside your [Nx workspace](https://nx.dev/). Whether you work in a large enterprise among diverse development teams using different technology stacks or just a startup wanting to make the right decision on how to build out your company's technology from square one, Nx provides peace of mind. The ability to scale out different stacks at anytime without leading your company directly into a corner is a decision your Managers and teammates will continue to thank you well into the future. We have experienced exactly this among our team at nstudio. Our [xplat](https://github.com/nstudio/xplat) tools enhance Nx to provide [Electron](https://electronjs.org/), [Ionic](https://ionicframework.com/), [NativeScript](https://www.nativescript.org/) and [Nest](https://nestjs.com/) support with an architecture that eases the ability to maximize code sharing between all these frameworks/platforms. From [Angular docs](https://angular.io/guide/elements): > Custom elements are a Web Platform feature currently supported by Chrome, Firefox, Opera, and Safari, and available in other browsers through polyfills (see Browser Support). A custom element extends HTML by allowing you to define a tag whose content is created and controlled by JavaScript code. The browser maintains a CustomElementRegistry of defined custom elements (also called Web Components), which maps an instantiable JavaScript class to an HTML tag. Let's look a new generator provided by xplat starting with version `7.4.0` to create custom elements from any Angular components in your workspace to use in another framework like React. ## Prepare Angular components to try If you'd like to follow along here you can run these commands to prepare a new workspace to try this out:
npm i -g @nrwl/schematics

create-nx-workspace mycompany
// At the prompts:
// "Which stylesheet format would you like to use?": Choose "SCSS" for this example.
// "What is the npm scope you would like to use for your Nx Workspace?": Type "mycompany"
// "What to create in the new workspace": Choose "empty"

cd mycompany
ng add @nstudio/schematics --prefix=foo --platforms=web

ng g app myapp --routing=true --style=scss
// At the prompts:
// "In which directory should the application be generated?": Just hit Enter key on keyboard to accept default "apps" directory.
// "Which Unit Test Runner would you like to use for the application?": Choose any you prefer.
// "Which E2E Test Runner would you like to use for the application?": Choose any you prefer.
// "Which tags would you like to add to the application?": Just hit Enter for this example.
// "What framework would you like to use for the application?": Choose "Angular"
At this point you can run `npm start` and open your browser at `http://localhost:4200` to see this: ![Starter Angular app](https://cdn.tipe.io/5a7276537cdf7d001322af1b/908a9f6f-027a-4e0a-9c7f-59e7c257ff50/Screen%20Shot%202019-04-01%20at%203.16.07%20PM.png) Let's first generate two Angular components which we'll end up converting to custom elements:
ng g component menu --platforms=web
ng g component footer --platforms=web
This will create `MenuComponent` and `FooterComponent` automatically declared by the web's `UIModule` which is a workspace shared module already included in any web app you generate with xplat. Modify `apps/web-myapp/src/app/features/home/components/home.component.html` to use them:
  

Nx

An open source toolkit for enterprise Angular applications. Nx is designed to help you create and build enterprise grade Angular applications. It provides an opinionated approach to application project structure and patterns.

Quick Start & Documentation

Watch a 5-minute video on how to get started with Nx.

{{ 'welcome' | translate }}!

Try things out

Learn more about xplat.
>
Now run the web app with: `npm start` and have a look. You should see that the menu and footer component appear with their default starting template on the page: ![Starter app with our custom components](https://cdn.tipe.io/5a7276537cdf7d001322af1b/5173199e-ca28-4faa-948e-85722dd2ffc1/comp-example.png) ## ng g elements The `elements` generator allows you to quickly generate custom elements from any shared Angular components in your workspace. It also includes a build script using [ngx-build-plus](https://github.com/manfredsteyer/ngx-build-plus) to quickly roll them up and drop into any other framework. Additionally a script is added to preview them after generation to confirm they work. Let's wrap up our menu and footer component as custom elements:
ng g elements ui-kit --barrel=@mycompany/web --components=menu,footer
This creates an Angular Element module at `libs/xplat/web/elements/ui-kit.module.ts` which defines your menu and footer component found inside the `@mycompany/web` barrel in our workspace as custom elements:
import { NgModule, Injector } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { createCustomElement } from '@angular/elements';
import { MenuComponent, FooterComponent } from '@mycompany/web';

@NgModule({
  imports: [BrowserModule],
  entryComponents: [MenuComponent, FooterComponent]
})
export class UiKitModule {
  constructor(private injector: Injector) {}

  ngDoBootstrap() {
    let component;
    component = createCustomElement(MenuComponent, { injector: this.injector });
    customElements.define('foo-menu', component);
    component = createCustomElement(FooterComponent, {
      injector: this.injector
    });
    customElements.define('foo-footer', component);
  }
}
The first time you use the `elements` generator it also adds build support via `ngx-build-plus`. Let's make sure it's installed:
npm install
Let's now build our elements for consumption:
npm run build.web.elements
This will use [ngx-build-plus](https://github.com/manfredsteyer/ngx-build-plus) to create a distributable always located at `dist/ngelements/main.js` file we can drop on any web page. We can now quickly preview our custom elements working:
npm run preview.web.elements
Open your browser to: http://localhost:8080 and you should see your Angular created components useable as custom web elements which can be used anywhere and with any web framework. ![Custom elements](https://cdn.tipe.io/5a7276537cdf7d001322af1b/cda975f8-f3a7-4ab3-a443-1d929e0094da/Screen%20Shot%202019-04-01%20at%203.32.26%20PM.png) ## Viola! Custom elements in a flash! Now let's say we want to wrap up a different set of components as a seperate build target of custom elements. Create two other Angular components to try:
ng g component dropdown --platforms=web
ng g component link --platforms=web
Now let's create custom elements from those:
ng g elements widgets --barrel=@mycompany/web --components=dropdown,link
You can now build and preview:
npm run build.web.elements
npm run preview.web.elements
The `elements` generator will always update the builder files to the last generated elements. ## How to switch the builder files to any custom element sets anytime? You can use the `--builderModule` argument to update the target at anytime.
ng g elements --builderModule=ui-kit
This will update the builder to use the `ui-kit.module` we created first. You can then build/preview which will show you the menu and footer elements from that module. ## Ready to distribute You will always find your custom elements bundled up in `dist/ngelements/main.js`. Feel free to rename, distribute or integrate into any of your workspace build processes. # Use inside a React app Now generate a React app in your workspace:
ng g app reactapp --framework=react
// At the prompts:
// "In which directory should the application be generated?": Just hit Enter key on keyboard to accept default "apps" directory.
// "Which Unit Test Runner would you like to use for the application?": Choose any you prefer.
// "Which E2E Test Runner would you like to use for the application?": Choose any you prefer.
// "Which tags would you like to add to the application?": Just hit Enter for this example.
You should now be able to run your React app with `npm run start.web.reactapp` and see this in browser: ![React app with Nx](https://cdn.tipe.io/5a7276537cdf7d001322af1b/6aaff067-eee1-4201-8bf1-c51f962c7562/Screen%20Shot%202019-04-01%20at%204.53.41%20PM.png) ## Use custom elements from our Angular app in our React app To prepare our React app to work with custom elements we need to add "intrinsic types" which is similar to using `CUSTOM_ELEMENTS_SCHEMA` in Angular as mentioned [by Nrwl in Build Angular and React applications together](https://blog.nrwl.io/building-angular-and-react-applications-together-with-nx-78b5578de598). Add a new file `apps/web-reactapp/src/intrinsic.d.ts` with the following contents (note: *this will likely be included by default with xplat in future for React*):
declare namespace JSX {
  interface IntrinsicElements {
    [elemName: string]: any;
  }
}
Let's also update `angular.json` which defines the build config for our React app to include the custom elements we generated above. We'll include the `polyfills` that our elements build created for us as well. You will find the `web-reactapp` configuration (shown in part here for brevity) where we update the `scripts` collection as follows to use our custom built web elements:
"web-reactapp": {
      "root": "apps/web-reactapp/",
      ...,
      "architect": {
        "build": {
          "builder": "@nrwl/builders:web-build",
          "options": {
            ...,
            "scripts": [
              "dist/ngelements/polyfills.js",
              "dist/ngelements/main.js"
            ]
          },
Now let's run our React app again with `npm run start.web.reactapp` and you should see this in the browser: ![Custom elements with Angular and React](https://cdn.tipe.io/5a7276537cdf7d001322af1b/c51109bf-542d-4149-9b64-c20aa81b3d0d/ng-react-elements.png) ## Conclusion Combining [Nx](https://nx.dev/) with [xplat](https://nstudio.io/xplat/) provides an extensible foundation allowing you and your team to scale at your own pace with preparation for all sorts of diverse situations your project may encounter now and well into the future. In a real world scenario you'd probably want to enhance your workflow which would rename and move your custom elements from `dist/ngelements/main.js` into each app's deploy directory. The possiblities are endless. If you're interested in learning more about custom elements we highly recommend [Juri Strumpflohner](https://twitter.com/juristr)'s course [Getting Started with Angular Elements](https://egghead.io/courses/getting-started-with-angular-elements) on egghead.io.