DEV Community

loading...
Cover image for Building Tailwind CSS in an Angular CLI project

Building Tailwind CSS in an Angular CLI project

beavearony profile image Michael Gustmann Updated on ・6 min read

If you are wondering how to use Tailwind CSS in an Angular CLI project and automatically create an optimized CSS bundle during production builds, you've come to the right place!

You can check out a working minimal solution in this repo

Angular CLI

Ejecting out of the Angular CLI seems like a nightmare to me. Too much of a benefit of generating code, building and configuring quickly. Many tools like NX build on top of it. Since version 6 of Angular it is not even possible to eject out of the CLI anymore. Instead we've got an extensible builder system to include customization, in case we need it.

The Angular CLI uses webpack under the hood to build and bundle the web app. WebPack might be replaced by another build tool, like Bazel, if it proves to be better. Most cases don't need to customize specific build details, but in our case, it is beneficial to hook into the build system. Fortunately, we have the ability to customize the webpack build by using custom builders.

With the help of @angular-builders/custom-webpack we can add some logic to extend the underlying webpack build.

Tailwind CSS

Tailwind CSS is an awesome library to remove the headache of coming up with class names. Everything I need in CSS is defined in HTML and unused styles can be stripped away at build time. I remember those times where I was afraid to remove CSS classes, because these might be used somewhere else. No more! The machine can do it for me.

We will setup an Angular CLI project to compile Tailwind CSS with the help of @angular-builders/custom-webpack to include a build step into the default webpack config. PostCSS will load our Tailwind root file and process it with additional vendor prefixes and remove unused classes with the PurgeCSS plugin. You can find more information about this process in the official Tailwind Documentation about Controlling File Size.

To not slow us down during development, we will configure it to only run the PurgeCSS process during production builds.

Separating Tailwind CSS from the rest

I find it better to separate Tailwind into its own file ./src/tailwind.scss instead of putting the Tailwind's base, components, and utilities styles directly into our CSS. This way I can target the custom build to only this file and leave the rest as it is. Other component libraries like Angular Material can be added without worrying about the intermingled build process.

We can also use whatever style system we like (ie. CSS, SCSS, LESS ...) in our app. Tailwind will just work, can be used in any template and will only put the styles we actually use into our final bundle. I don't see a reason to write any CSS in any components with this setup.

If you want to change the default ng generate component component to NOT create the styles file, you can use this command to default to inline styles in your app:

ng config projects.<my-project>.schematics.@schematics/angular:component.inlineStyle true

Set it up

Create the Angular Project

Install the Angular CLI, if you haven't done so:

npm i -g @angular/cli

Create a new Angular CLI project:

ng new tailwindcss-angular
cd tailwindcss-angular

Install dependencies

Install the packages into the project:

npm i tailwindcss
npm i -D @angular-builders/custom-webpack @fullhuman/postcss-purgecss

Initialize Tailwind CSS

Create a tailwind.config.js file by running this command:

npx tailwind init

This can be used to customize Tailwind with themes, colors, breakpoints, spacing and many others.

Create a new file src/tailwind.scss with this content:

@tailwind base;
@tailwind components;
@tailwind utilities;

Customize the build process

Create another new file extra-webpack.config.js in the root folder of our project and add this content:

const purgecss = require('@fullhuman/postcss-purgecss')({
  // Specify the paths to all of the template files in your project
  content: ['./src/**/*.html', './src/**/*.component.ts'],
  // Include any special characters you're using in this regular expression
  defaultExtractor: content => content.match(/[\w-/:]+(?<!:)/g) || []
});

module.exports = (config, options) => {
  console.log(`Using '${config.mode}' mode`);
  config.module.rules.push({
    test: /tailwind\.scss$/,
    use: [
      {
        loader: 'postcss-loader',
        options: {
          plugins: [
            require('tailwindcss')('./tailwind.config.js'),
            require('autoprefixer'),
            ...(config.mode === 'production' ? [purgecss] : [])
          ]
        }
      }
    ]
  });
  return config;
};

Autoprefixer automatically adds vendor prefixes. Leave it out, if you don't need this.

Configure angular.json

Then, to use these files open up angular.json and look for the "architect" section. You need to customize the ng build and ng serve parts.

You can do it by editing the angular.json file directly or use the ng config command.

So you do this twice, once for build and once for serve:

  1. Replace @angular-devkit/build-angular with @angular-builders/custom-webpack
  2. Add the customWebpackConfig path to the options

Make changes to the build section

ng config projects.<my-project>.architect.build.builder @angular-builders/custom-webpack:browser
ng config projects.<my-project>.architect.build.options.customWebpackConfig.path extra-webpack.config.js

Make changes to the serve section

ng config projects.<my-project>.architect.serve.builder @angular-builders/custom-webpack:dev-server
ng config projects.<my-project>.architect.serve.options.customWebpackConfig.path extra-webpack.config.js

Add the tailwind.scss file to the styles array

Don't forget to add the tailwind.scss path to the styles array. This is probably best done directly in the angular.json file. In case you want to do it with a command, this will add the file to the second index of the array or replace anything that is configured in that slot. So be careful not to overwrite anything.

ng config projects.<my-project>.architect.build.options.styles[1] src/tailwind.scss

Summarized changes to angular.json

In total there are 9 lines affected. The final outcome of all our changes should look like this:

"architect": {
  "build": {
    "builder": "@angular-builders/custom-webpack:browser", // ←
    "options": {
      "customWebpackConfig": {            // ←
        "path": "extra-webpack.config.js" // ←
      },                                  // ←
      // ...
      "styles": [
        "src/tailwind.scss",              // ←
        "src/styles.css"
      ],
    // ...
    }
  },
  "serve": {
    "builder": "@angular-builders/custom-webpack:dev-server", // ←
    "options": {
      "customWebpackConfig": {            // ←
        "path": "extra-webpack.config.js" // ←
      },                                  // ←
    // ...
    }
  }
}

Test it out

Everything should be in place now. To see if if it worked correctly, replace the content of ./src/app.component.html with some HTML that uses Tailwind CSS classes or use this:

<div class="max-w-lg mx-auto bg-white shadow-lg rounded-lg overflow-hidden">
  <div class="sm:flex sm:items-center px-6 py-4">
    <div
      class="flex items-center justify-center sm:flex-col sm:w-24 sm:border sm:border-gray-500 sm:rounded-full"
    >
      <img
        class="block sm:mx-auto mx-0 flex-shrink-0 h-16 sm:h-12"
        src="https://angular.io/assets/images/logos/angular/angular.svg"
        alt="Angular Logo"
      />
      <img
        class="block sm:mx-auto mx-0 flex-shrink-0 h-16 sm:h-12"
        src="https://tailwindcss.com/android-chrome-512x512.png"
        alt="Tailwind CSS Logo"
      />
    </div>

    <div class="mt-4 sm:mt-0 sm:ml-4 text-center sm:text-left">
      <p class="text-xl leading-tight">Tailwind CSS & Angular CLI</p>
      <p class="text-sm leading-tight text-gray-600">
        With custom webpack configuration!
      </p>
      <div class="mt-4">
        <button
          class="text-purple-500 hover:text-white hover:bg-purple-500 border border-purple-500 text-xs font-semibold rounded-full px-4 py-1 leading-normal"
        >
          GO
        </button>
      </div>
    </div>
  </div>
</div>

To see the result in a web browser run ng serve. To see the build output run ng build --prod and take a look into the dist/tailwindcss-angular/styles.########.css file.

Tailwind CSS automatically adds normalize.css. You will see these styles in the beginning of the file.
It will also only contain the css classes used by your app. Everything else is stripped out.

Summary

We learned how to make changes to the build process of our Angular CLI project to compile Tailwind CSS with just a few simple steps.

  1. Install the custom-webpack, purgecss and tailwindcss dependencies
  2. Init tailwindcss and create a tailwind.scss file
  3. Create a extra-webpack.config.js file with the build config
  4. Adjust angular.json

Happy styling your Angular components without writing CSS!

Discussion

pic
Editor guide
Collapse
dsebastien profile image
Sébastien D.

Great article, thanks Michael!

I've written an article inspired by yours, explaining how to integrate Tailwind in a Nrwl NX project and with Storybook as well: medium.com/@dSebastien/adding-tail...

Collapse
alexbjorlig profile image
Alex Bjørlig

I was trying to follow this guide, but I get an error with the webpack config file saying: `An unhandled exception occurred: Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.

  • configuration has an unknown property 'call$1$0'. These properties are valid (...). Any ideas - copy pasted the extra-webpack.config.js` file, using Angular 10
Collapse
jboothe profile image
Jeff Boothe

Fine Job, Michael!

The genius of this post is the section: "Separating Tailwind CSS from the rest".

For those of you who have struggled with the other 4 or 5 other Angular/Tailwind examples out there - save your yourself the headache of installing additional postcss loaders to accommodate your custom or nested scss, or file/image loaders to find relative paths like, url(my.png), and the same with font references. Phew!

It just makes so much sense to isolate Tailwind and target it only with a custom webpack config.

Again, Kudos!

Collapse
lhargil profile image
Lhar Gil 🇵🇭 🇲🇾☕📷

This is a very helpful post. Thanks a lot Michael.

There's a bit of a typo though, but it's no biggie. On the section of modifying the serve section,

ng config projects..architect.serve.builder @angular-devkit/build-angular:dev-server

it should be,
ng config projects..architect.serve.builder @angular-builders/custom-webpack:dev-server

Collapse
beavearony profile image
Michael Gustmann Author

Oh, thanks a lot for catching it. Fixed.

Collapse
ojiepermana profile image
ojiepermana

I get error in angular 9 :

ERROR in Cannot read property 'flags' of undefined

how ti fixed ?,

thanks

Collapse
nicowernli profile image
Nicolás Wernli

Did you fixed it? I'm facing the same issue

Collapse
Sloan, the sloth mascot
Comment deleted
Collapse
beavearony profile image
Michael Gustmann Author

Actually this works exactly like described in the post. The styles.(s)css where material is added is not touched by this approach.
So, after you completed the steps above do

ng add @angular/material

answer a few questions and you're done.

Collapse
jahnreektor profile image
jahnreektor

Unfortunately, ng add doesn't work because we have changed the builder:

Your project is not using the default builders for "build". The Angular Material schematics cannot add a theme to the workspace configuration if the builder has been changed.

Thread Thread
georgeisbusting profile image
Stephen Ferjanec

You can add the styles in yourself:

For example, under architect, build, add indigo-pink.css to your styles array:
"styles": [
"src/styles.css",
"src/tailwind.scss",
"node_modules/@angular/material/prebuilt-themes/indigo-pink.css"
],

Collapse
oncletim profile image
L'oncle Tim

Thanks @beavearony ! Now we can do this automaticaly with schematics 😇
ng add @garygrossgarten /ngx-tailwind

Collapse
richardthombs profile image
Richard Thombs

Thank you very much for this Michael!

Collapse
anassl profile image
AnassL

Hi Michael,
I've seen new angular 9 apps using Tailwindcss without the need to touch webpack, or to install @angular-builders/custom-webpack, how do they work ?

Collapse
anassl profile image
AnassL

Oh, sorry, I just got that this tutorial was about triggering simultaneously Tailwind build at "ng serve" command execution.

Collapse
rudyhadoux profile image
rudyhadoux

Useful, thanks.

Collapse
antolaba profile image
antolaba

Hi! I need to import my custom-utilities-components in my project. I import my file into tailwild.scss but it doesn't work.
Link: tailwindcss.com/docs/adding-new-ut...

Collapse
andrhevictor profile image
Andrhé Victor

Thank you. Very straigthforward.

Collapse
odlir profile image
Odlir

Hi Thanks Michael, this is what i was looking for days! i followed all steps however it seems that my tailwind.scss file is not importing tailwind library. anyidea why this could be happening?

Collapse
odlir profile image
Odlir

nevermind! i reran ng serve again and it worked! :)

Collapse
jimlynchcodes profile image
Jim Lynch

Thank you Michael for this non-shit guide! 😁