Tailwind CSS Purge: Optimize Angular for Production
Tags: tailwindcss, angular, scully
Published: 2020-12-15 09:56:02

---
title: Tailwind CSS Purge: Optimize Angular for Production
published: true
date: Tue, 15 Dec 2020 09:55:51 GMT
tags: tailwindcss, angular, scully
canonical_url: https://notiz.dev/blog/tailwindcss-purge-optimize-angular-for-production
---

By design, [Tailwind CSS](https://tailwindcss.com) generates a **large amount** of utility classes for your development build. For your Angular application you want the best performance by **only** including the classes you are actually using in your production build. Tailwind got you covered! [PurgeCSS](https://purgecss.com/) is build-in making it easy to tree-shake **unused** Tailwind styles for your application.

You are going to learn how to setup Tailwind's `purge` option to [optimize](https://tailwindcss.com/docs/optimizing-for-production) Tailwind CSS in your [Angular](https://notiz.dev/blog/angular-10-with-tailwindcss) and for your [Scully](https://notiz.dev/blog/jamstack-angular-scully-tailwind-css) Jamstack application.

The `purge` options in this post have been tested with Angular 11 and Tailwind CSS 2.0, it also works with Angular 10 and Tailwind CSS 1.9.

## Get Started

Get ready with a new or existing Angular + Tailwind CSS application

```bash
ng new app-name

# add tailwind
ng add ngx-tailwind

# optional - add jamstack with Scully
ng add @scullyio/init
```

Now [use Tailwind CSS utility classes](https://notiz.dev/blog/angular-10-with-tailwindcss#use-tailwind-css-utility-classes) in your Angular application HTML template, using `apply` in your stylesheets or even in your TypeScript files via `@HostBinding(...)`.

## Purge unused Tailwind CSS utilities in Angular

Tailwind provides a [purge](https://tailwindcss.com/docs/optimizing-for-production#removing-unused-css) option in the `tailwind.config.js` file. Purge removes **only** classes generated by [Tailwind](https://tailwindcss.com/docs/optimizing-for-production#removing-all-unused-styles) or styles added to the `@layer` [directive](https://tailwindcss.com/docs/functions-and-directives#layer). Custom CSS or third-party CSS like Angular Material or Prism.js will not be removed.

Simply provide all your template paths as an array to the `purge` option. For an Angular application this would be all HTML and TS files in your `src` directory. TS files should be included as they might reference class names using e.g. `@HostBinding(...)`.

```js
module.exports = {
  purge: ["./src/ **/*.html", "./src/** /*.ts"],
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [],
};
```

Use `*.{html,ts}` to match multiple file types in the same directory

```diff
module.exports = {
- purge: ["./src/ **/*.html", "./src/** /*.ts"],
+ purge: ["./src/**/*.{html,ts}"],
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [],
};
```

Trigger Tailwind to automatically purge your CSS by setting `NODE_ENV=production` during your `ng build` step. If you used [ngx-tailwind](https://github.com/notiz-dev/ngx-tailwind) schematics to setup Tailwind it automatically added a production script to your **package.json**. Additionally, the latest release of [ngx-tailwind@1.1.0](https://github.com/notiz-dev/ngx-tailwind/releases/tag/v1.1.0) adds the above purge options automatically to your `tailwind.config.js`.

```json
{
  "scripts": {
    "build:prod": "NODE_ENV=production ng build --prod"
  }
}
```

Now run `npm run build:prod` to **only** include used Tailwind CSS utilities in your Angular production build. This even works great in your Scully application

## Advanced Purge options

`purge` also accepts an options object for further optimizations. Available `purge` options are [enabled](https://tailwindcss.com/docs/optimizing-for-production#enabling-manually), `content` for your template paths, [preserveHtmlElements](https://tailwindcss.com/docs/optimizing-for-production#preserving-html-elements), [layers](https://tailwindcss.com/docs/optimizing-for-production#purging-specific-layers), [mode](https://tailwindcss.com/docs/optimizing-for-production#purging-specific-layers) and last [options](https://tailwindcss.com/docs/optimizing-for-production#purge-css-options) to pass it directly to PurgeCSS. The defaults for these options are:

```js
module.exports = {
  purge: {
    // enabled: true, // enabled by `NODE_ENV=production` or enable manually
    mode: 'layers', // or 'all' ☠️ be careful
    preserveHtmlElements: true, // or false ⚠️ not generally recommended
    layers: ['base', 'components', 'utilities'], // remove layers to ignore from purging
    content: [], // add your template paths
    options: { /* PurgeCSS options */}
  },
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [],
};
```

Too use the object syntax for the `purge` option add the template paths to the `content` option

```js
module.exports = {
  purge: {
    content: ["./src/**/*.{html,ts}"]
  },
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [],
};
```

Go ahead and provide additional options to the `purge` object to further optimize your production build based on your application. For specific configurations pass it directly to [PurgeCSS](https://purgecss.com/configuration.html#options) using the `options` key. You can provide `safelist`, `blocklist`, `extractors` and more.

```js
module.exports = {
  purge: {
    content: ["./src/ **/*.html", "./src/** /*.ts"],

    // These options are passed through directly to PurgeCSS
    options: {
      safelist: ['bg-red-500', /^mat-/],
      blocklist: ['bg-orange-500', /^cdk-/],
      extractors: [],
      ...
    }
  },
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [],
};
```

Let's improve purging for a Scully application by writing an `extractor` for your Markdown content files to detect which HTML tags and CSS classes are **actually** used.

## Purge Scully Markdown content

Scully organizes the content of your static-site in Markdown files. Add the path to your Markdown files e.g. `'./blog/**/*.md'` to the `content` array. Create an [extractor](https://purgecss.com/extractors.html#default-extractor) targeting only files with the Markdown extension `md`.

```js
module.exports = {
  purge: {
    content: ['./src/ **/*.{html,ts}', './blog/** /*.md'],
    options: {
      extractors: [
        {
          extensions: ['md'],
          extractor: (content) => {

            return [];
          },
        },
      ],
    },
  },
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [],
};
```

Before matching HTML elements and CSS classes you need to parse the Markdown content to HTML. Scully uses [marked](https://github.com/markedjs/marked) to parse your Markdown content files. Let's require it in the `tailwind.config.js` and parse the content in the extractor.

```js
const marked = require('marked');

module.exports = {
  purge: {
    content: ['./src/ **/*.{html,ts}', './blog/** /*.md'],
    options: {
      extractors: [
        {
          extensions: ['md'],
          extractor: (content) => {
            content = marked(content);

            return [];
          },
        },
      ],
    },
  },
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [],
};
```

Let's use the Regex used by [blog.tailwindcss.com](https://github.com/tailwindlabs/blog.tailwindcss.com/blob/364b3713862b6d309aff79505776a9948ad26620/tailwind.config.js#L5-L27) to find all used HTML elements and classes. Also set the `mode: 'all'` ☠️ and `preserveHtmlElements: false` ⚠️ to remove unused tags like `h4` and more.

```js
const marked = require('marked');

module.exports = {
  purge: {
    model: 'all',
    preserveHtmlElements: false,
    content: ['./src/ **/*.{html,ts}', './blog/** /*.md'],
    options: {
      extractors: [
        {
          extensions: ['md'],
          extractor: (content) => {
            content = marked(content);

            // Capture as liberally as possible, including things like `h-(screen-1.5)`
            const broadMatches = content.match(/[^<>"'`\s]*[^<>"'`\s:]/g) || []

            // Capture classes within other delimiters like .block(class="w-1/2") in Pug
            const innerMatches =
              content.match(/[^<>"'`\s.(){}[\]#=%]*[^<>"'`\s.(){}[\]#=%:]/g) || []

            return broadMatches.concat(innerMatches);
          },
        },
      ],
    },
  },
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [],
};
```

Perfect, now your Angular or Scully applications are **optimized** for production and you are ready to deploy it to [Firebase Hosting](/blog/firebase-hosting-preview-deploy) or other services.