Published on

Keeping the Wind in Your Wings

Authors

This article was originally posted on Medium. It is quite probable that some of the commands listed below are outdated at this point. I tried to include links to all the relevant technologies, so you can self-correct as you go.

How to Add Tailwind CSS to Angular CLI or Create React App Projects

If you're into new and shiny Javascript-based development phenomena, then perhaps you've heard of Tailwind CSS, a robust and configurable utility framework maintained by Adam Wathan, Jonathan Reinink, David Hemphill and their growing open source community. Perhaps you've also heard of Angular or React, and their extremely helpful cli-tools for getting new projects off the ground quickly (Angular CLI and Create React App). This article intends to reveal one problem you may run into when using Tailwind in an app based on either of these CLI projects, and how to overcome that in a few simple steps so your development cycles stay lean and mean (or MERN, if you're into that sort of thing…).

The Problem

One of the great strengths of Tailwind is its endless customizability, and how this is seamlessly integrated into the development workflow via PostCSS plugins which play very nicely with most build configurations. If you enjoy the whole hit-save-browser-auto-refresh thing, the folks who designed Tailwind do too, and they made the build process lightning fast.

If you know anything about the webpack configurations behind Angular CLI and Create React App, you may have noticed that they use PostCSS already, and so it would seem obvious enough to use Tailwind "as intended" in the webpack configuration and call it a day!

Unfortunately, that is exactly what Angular CLI and Create React App are designed to prevent you from doing. All of the configuration decisions are pre-made by the teams who created these projects (guided by the consensus on best practices, of course), so you can focus on just building your app. And in 90+% of cases, I'll wager, that is exactly what you need. Webpack is complicated—and powerful—enough to create an incredibly interesting, albeit tedious, rabbit hole for any developer, so keeping you away from it to focus on your components and services is a real perk. …Until you have an awesome CSS library like Tailwind that is designed with configurability in mind, from PostCSS plugins to PurgeCSS integration. After all, Tailwind's primary use case is custom UI styling, which generally defies an "out of the box" approach to tooling and development workflow.

The Quick (and Dirty) Fix

If you are using Angular CLI pre 6.0.3 or Create React App, then you can simply eject your project from the cli tool and regain complete control over any and all possible configuration files and build tools. In the case of Create React App, especially for experienced devs, this isn't too bad (maybe?). However, Angular CLI has far too many features that enable developer efficiency for that to be a good option. I, for one, was completely dissatisfied and disappointed by that tradeoff. So, I built a solution.

Having Your Cake and Eating It Too

If you came here for a quick solution so you can dive into the wonderful world of Tailwind with a fresh Angular or React project, then here you go: ng-tailwindcss. The readme provides you with everything you need to know about how the project works and how to get started. Just a few terminal commands and you're in business. It really is that easy. You're welcome.

However, maybe for one reason or another you need more than just another npm package to feel satisfied (but please do have a look at it). The rest of this article is for you. Enjoy.


Gettin' Under the Hood

First of all, some more credit to the Adam Wathan and the Tailwind team for making Tailwind's build process so flexible. Not only does it work magically as a PostCSS plugin, but it also comes with it's own standalone CLI.

Installation

To install Tailwind in an Angular project (see notes below for React), simply run

npm i -D tailwindcss
npx tailwind init # creates ./tailwind.js (config file)
touch src/tailwind.css

In src/tailwind.css, you'll place your tailwind imports. You have completed the installation process! Yay! Now any CSS you want to add can be put in that tailwind.css file and it will come along for the ride during the build step.

Building Tailwind

All of those nifty utility classes are dynamically generated by server-side Javascript (NodeJS) based on the tailwind.js configuration, so any change made to that file requires that Tailwind be rebuilt to include the change in your final CSS. The build command is

npx tailwind build src/tailwind.css -c ./tailwind.js -o ./src/styles.css

This essentially takes the CSS from tailwind.css combined with the configuration file (./tailwind.js) and builds the full stylesheet with all the appropriate utility classes in src/styles.css.

In an Angular project, the src/styles.css file is the default global stylesheet, so once our CSS builds to this file, we're off to the races.

But Wait, There's More!

Interestingly enough, we've just solved one of our problems! In our package.json, we have a script for building our project. This is just a bash command that leverages the Angular CLI, so we can take the Tailwind build script from above and insert it before the rest of the build command and—presto change-o—we now have Tailwind fully integrated into any build of our Angular app without any additional configuration or terminal work!

// fragment of package.json with tailwind build step
{
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "npx tailwind build src/tailwind.css -c ./tailwind.js -o ./src/styles.css && ng build"
...
}
...
}

Full Integration

Having the build process set is nice, but what about our development flow? We could join the Tailwind build command with&& ng serve, but that would only build our CSS once when we run npm start. What if we make a change to tailwind.js? Angular doesn't know about our extra build requirement, so nothing would happen.

But what Angular does know is the state of src/styles.css. While ng serve is running, that file is one of many being watched by Angular CLI for changes, and upon detecting them, the development server will reload the affected modules and refresh the browser tab that is displaying our app. This is perfect for our situation, because every time the Tailwind build process runs, src/styles.css gets updated, which will trigger the desired reload!

One solution, at this point, would be to create another npm script, or maybe a bash alias, for the Tailwind build command, so we can quickly run it whenever we need a change to our tailwind.js or src/tailwind.css files to be included in our local app. But come on, we're engineers! Automate that stuff!

Let's make these files a first-class citizen of our development workflow, just like the rest of our app files, and create a file-watcher in NodeJS that will detect any save events for these files and launch the Tailwind build step for us. For file-watching, I'll use Chokidar, and for the build script, we'll grab the exec method from the native child_process module.

// tailwind-serve.js
const { exec } = require('child_process')
const chokidar = require('chokidar')
const watcher = chokidar.watch([
'./src/tailwind.css',
'./tailwind.js'
])
watcher.on('change', (event, path) => {
console.log('Processing changes to Tailwind files')
exec('npx tailwind build src/tailwind.css -c ./tailwind.js -o ./src/styles.css', err => {
if (err) console.error(err)
else console.log('Successful Tailwind Build')
})
})

And there we have it, running this file will create an event listener on those two files, and in our handler we are calling the build script. We can now put this in our start script like so:

// package.json
{
"scripts": {
"start": "node tailwind-serve.js & ng serve"
...
}
}

Notice the single & between the two bash commands: this will cause both processes to run concurrently, whereas && causes the processes to run sequentially, contingent upon the success of the previous process (errors short-circuit the whole thing).

We can now fire up our Angular + Tailwind project in dev mode with npm start, and create a build with npm run build. Hooray!

Quick Note About React

Our example above was referencing an Angular project, but the only substantive differences between the two frameworks (for our purposes here) are:

The default global stylesheet is usually src/index.css (use that anywhere we were using src/styles.css with Angular).

The scripts in package.json are different, but we will still use the same technique:

"start": "node tailwind-serve.js & react-scripts start",
"build": "npx tailwind build src/tailwind.css -c ./tailwind.js -o ./src/index.css && react-scripts build"

If you are using ng-tailwindcss, you can use the configure command to adjust the file names and it should work with React beautifully. I might yet work in a React-specific command for the scripts, but until then, doing it manually ain't so bad:

"prestart": "ngtw b",
"start": "ngtw w & react-scripts start",
"build": "ngtw b && react-scripts build"

Conclusion

Tailwind CSS is an incredible utility framework that is rapidly growing in popularity. If you are going for a custom look on your next Angular or React project, you really ought to give it a shot. Hopefully, this article (and ng-tailwindcss) has helped you see past the challenge of not having webpack access in Angular CLI or Create React App.

At the time of writing, Adam Wathan and the crew are gearing up for the 1.0.0 release, which is really exciting! Send him an encouraging word over social media for the quality work he's doing for the the dev community, and have a look at the discord server for more information on Tailwind and its growth.

Comments

    Nothing yet. to share your thoughts!