notiz/blog/Tailwind CSS Purge: Optimize Angular for Production
Published at

Tailwind CSS Purge: Optimize Angular for Production

Remove unused Tailwind CSS utilities from your Angular production build for best performance

By design, Tailwind CSS 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 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 Tailwind CSS in your Angular and for your Scully 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

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 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 option in the tailwind.config.js file. Purge removes only classes generated by Tailwind or styles added to the @layer directive. 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(...).

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

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 schematics to setup Tailwind it automatically added a production script to your package.json. Additionally, the latest release of ngx-tailwind@1.1.0 adds the above purge options automatically to your tailwind.config.js.

{
  "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, content for your template paths, preserveHtmlElements, layers, mode and last options to pass it directly to PurgeCSS. The defaults for these options are:

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

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 using the options key. You can provide safelist, blocklist, extractors and more.

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 targeting only files with the Markdown extension md.

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 to parse your Markdown content files. Let's require it in the tailwind.config.js and parse the content in the extractor.

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 to find all used HTML elements and classes. Also set the mode: 'all' ☠️ and preserveHtmlElements: false ⚠️ to remove unused tags like h4 and more.

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 or other services.

Weeklyish Newsletter

Stay up to date with Angular, NestJS and more! Sign up to receive updates on our latest posts.

Blog