Making Stenciljs library work with Tailwindcss inside an Nx Workspace

In this guide I will explain my reasons and how I did manage to find the best way to make Stenciljs project work inside an Nx workspace and how to use the Stencil components work inside your apps in React or Angular there.

Introduction and the problem

I will explain my reasons and what each thing is, if you already know what Nx, Tailwindcss, and Stencijs are, you can step over and go to the instructions (Final Solution) of how to make them work together.

TL;DR Stenciljs is awesome but can be tricky making it work as a lone project, Nx will help you with that, speeding up the development process. Tailwind can be tricky to configure in this scenario but I will teach you some tricks.

As you probably know, Stenciljs is a component library that generates web-components so you can use them in any web framework, increasing the lifespan of the whole thing.
Some weeks ago I started to learn Stenciljs and ended up falling in love with how much useful it is when we are building a Design System or even a PWA or an application.

As soon I started to want to try my new fancy components in my project, I realized that any time I created a new component fix a small bug, I would need to publish it as an npm package and update my project to get that. What a nightmare!

Another way to do that is through npm-link or npm-pack, using these ones I would not need to publish it to the npm, but I would yet need to run some commands to get the updates.

Making it very simple, Nx will create a workspace for your projects, so you can keep them in a single git repository (a.k.a monorepo). In this scenario, all the projects are easily connected so any changes in the Stenciljs project will be reflected automatically in the projects inside the workspace, no need to publish, linking or packing it.
Under the hood, Nx makes use of TypeScript’s path mapping feature, this allows you to write your import statements just as you would with a published package, but behind the scenes, TypeScript maps the imports to their given source code location.
Something like this in the tsconfig.json file:

{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"ui-components": ["libs/ui-components"],
"ui-components/loader": ["libs/ui-components/dist/loader/index.cjs.js"],
"ui-components-react": ["generated/ui-components-react/src/components.ts"]
},
....

So here I am trying Stenciljs and Nx and reached the point where I wished to use Tailwindcss instead of working with pure CSS (or even any CSS framework like Material Design, Bulma, bootstrap, etc.).

Tailwindcss is awesome, it is a utility-first CSS framework packed with classes like flex, pt-4, text-center and rotate-90 that can be composed to build any design, directly in your markup. With that, I can create my own components and make them have a look and feel more unique and near to my product without having to work so hard with CSS. It also has a great purge feature so it is tree-shakable!

Final Solution — The How-To Guide you were looking for

Ok, let's get it started!
1) Let's first create our Nx workspace

npx create-nx-workspace@11 --preset=react

It will ask you for the workspace, the react application name, stylesheet format, and Nx Cloud. Here are my answers.

Ok, the workspace was generated.
Now let's generate our Stenciljs library! First, we need to install its Nx extension plugin. You can find the documentation here.

cd myworkspace
npm install @nxext/stencil --save

Now with the extension installed, you will run the Nx command to generate the Stenciljs project inside the workspace.
I will name my stencil project as "design-system":

nx g @nxext/stencil:library design-system

When asked about the stylesheet, select SASS(.scss).

Now we need to make our library buildable and make it able to generate angular/react libraries for our stencil libraries using stencils "outputtargets"

nx g @nxext/stencil:make-lib-buildable design-system

When asked about the stylesheet, select SASS(.scss).
And finally, configure the output target to the frameworks you want to use it

nx g @nxext/stencil:add-outputtarget design-system

It will ask about the framework, we created a project called "frontend" and it is a react project, so we pick react here. In case you have an angular project, you can select angular.

Now let's install the Tailwind extension

npm install @nxext/tailwind --save-dev

Great, now an important step, install this package in order to integrate Stenciljs with tailwindcss. It provides simple functionality for supporting a utility-first workflow within the Shadow DOM.

npm install stencil-tailwind --save-dev

Next, within the project’s stencil.config.js file, import the plugin, and add it to the config's plugins config:

import { Config } from '@stencil/core'
import tailwind from 'stencil-tailwind'

export const config: Config = {
plugins: [
tailwind()
]
}

Note, hot module reloading (hmr) is not yet supported. For local development, you'll need to update reloadStratgy to use the pageReload option:

export const config: Config = {
...
devServer: {
reloadStrategy: 'pageReload'
}
}

Now, let's create our tailwind config file, so we can make customizations to our theme, one of the nicest things about tailwindcss.

npx tailwindcss init

This will generate a tailwind.config.js file at the root of your workspace.

Check the documentation for more configurations.

Let's make a small change in the my-component.tsx to insert some tailwind classes.

...
render() {
return <div class="bg-red-600 w-screen hover:bg-blue-300">Hello,World! I'm {this.getText()}</div>;}

We added a background red color, made the div's width fill the whole screen, and make it turn light blue when the mouse is "hover".

Now let's run our Stenciljs project

nx serve design-design

Now you see your nice component shining like a mighty s… ok! You get it!

But, we have an important step missing. How to use it in our react project?

In our scenario, we have a react project, so the easiest way to include the component library is to call defineCustomElements() from the maint.tsx file. Note that in this scenario applyPolyfills is needed if you are targeting Edge or IE11.

Go to your tsconfig.base.json and inside the node "path" add the path to the loader of our Stenciljs build. This is generated when we build or serve the library.

"paths": {
"@myworkspace/design-system": ["libs/design-system/src/index.ts"],"@myworkspace/design-system-react": ["libs/design-system-react/src/index.ts"],
"@myworkspace/design-system/loader" : ["dist/libs/design-system/loader/index.cjs.js"]}

What we're doing here is to tell where it should look when we write “@myworkspace/design-system/loader”. So now let's use it.

In the main.tsx do the following:

import React from 'react';import ReactDOM from 'react-dom';import { applyPolyfills, defineCustomElements } from '@myworkspace/design-system/loader';import App from './app/app';
ReactDOM.render( <React.StrictMode> <App /> </React.StrictMode>, document.getElementById('root'));applyPolyfills().then(() => {
defineCustomElements();
});

If you want your custom elements to be able to work on older browsers, you should add the applyPolyfills() that surround the defineCustomElements() function, otherwise you can just call defineCustomElements();

PS: If you are using an Angular project instead of React, you can follow the official docs in Stenciljs to make it work. But remember you'll need to configure the outputtargets for angular like this:

nx g @nxext/stencil:add-outputtarget design-system --outputType='angular'

Awesome, now you are good to go and try your fancy new Stenciljs components.

The repo with all we did here:
https://github.com/davidrock/nx-stencil-tailwind-react-angular

Software Engineer, Angular, Vue, React, .Net Core, EF Core, C#, Kotlin, Typescript, Javascript, and other cool stuff.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store