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:
- Replace @angular-devkit/build-angular with @angular-builders/custom-webpack
- 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.
- Install the custom-webpack, purgecss and tailwindcss dependencies
- Init tailwindcss and create a
tailwind.scss
file - Create a
extra-webpack.config.js
file with the build config - Adjust
angular.json
Happy styling your Angular components without writing CSS!
Top comments (21)
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...
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.
. Any ideas - copy pasted the
extra-webpack.config.js` file, using Angular 10Fine 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!
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
Oh, thanks a lot for catching it. Fixed.
I get error in angular 9 :
ERROR in Cannot read property 'flags' of undefined
how ti fixed ?,
thanks
Did you fixed it? I'm facing the same issue
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
answer a few questions and you're done.
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.
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"
],
Thanks @beavearony ! Now we can do this automaticaly with schematics 😇
ng add @garygrossgarten /ngx-tailwind
Thank you very much for this Michael!
Useful, thanks.
Thank you. Very straigthforward.
Some comments may only be visible to logged-in visitors. Sign in to view all comments.