purgecss: Purgecss breaks with nuxt and vuetify

I followed the instructions on setting up purgecss with nuxt and added it to simple vuetify nuxt project.

https://github.com/husayt/nuxt-vuetify-purgecss

As you can see with npm run build and npm run start layout just breaks apart.

Vuetify is the most popular material ui framework for vue and it would be great to get purgecss working with it

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 38 (5 by maintainers)

Most upvoted comments

I managed to remove a lot of unused CSS in my Nuxt + Vuetify + Tailwind app. It took me a while to figure out but I ended adding these options to my purgeCSS configuration which matched my requirements pretty nicely.

    whitelist: ['v-application', 'v-application--wrap'],
    whitelistPatterns: [/^v-((?!application).)*$/, /^theme--*/, /.*-transition/],
    whitelistPatternsChildren: [/^v-((?!application).)*$/, /^theme--*/],

I basically wanted to get rid of all v-application--is-rtl classes plus all vuetify utility classes and use tailwinds. My CSS is now ~83kb rather than ~245kb. You could also whitelist just theme--light if you would like to remove the dark theme styles.

I did also disable $color-pack. https://vuetifyjs.com/en/styles/colors/#sass-color-pack

$color-pack: false;

@patrickxchong Thx! I ended up with the same result as in my example. So it works 😆

I will leave this for posterity.

My App:

  1. Without all the nuxt-purgecss = parsed All (890.96 KB), gzipped All (208.99 KB)
2. Only buildModules: nuxt-purgecss (without extractCSS and @fullhuman/postcss-purgecss)
  buildModules: [
    [
      'nuxt-purgecss',
      {
        paths: [
          'node_modules/@nuxtjs/vuetify/**/*.ts',
          'node_modules/@nuxt/vue-app/template/**/*.html',
          'node_modules/@nuxt/vue-app/template/**/*.vue',
        ],
        whitelist: ['v-application', 'v-application--wrap'],
        whitelistPatterns: () => [
          /^v-((?!application).)*$/,
          /^\.theme--light*/,
          /.*-transition/,
        ],
        whitelistPatternsChildren: [/^v-((?!application).)*$/, /^theme--*/],
      },
    ],
parsed All (551.17 KB), gzipped All (167.62 KB)
3. buildModules + extractCSS (without @fullhuman/postcss-purgecss)
  buildModules: [
    [
      'nuxt-purgecss',
      {
        paths: [
          'node_modules/@nuxtjs/vuetify/**/*.ts',
          'node_modules/@nuxt/vue-app/template/**/*.html',
          'node_modules/@nuxt/vue-app/template/**/*.vue',
        ],
        whitelist: ['v-application', 'v-application--wrap'],
        whitelistPatterns: () => [
          /^v-((?!application).)*$/,
          /^\.theme--light*/,
          /.*-transition/,
        ],
        whitelistPatternsChildren: [/^v-((?!application).)*$/, /^theme--*/],
      },
    ],
  ],
  build: {
    analyze: true,
    extractCSS: true,

parsed All (532.1 KB), gzipped All (162.71 KB)


4. buildModules + extractCSS + @fullhuman/postcss-purgecss
  buildModules: [
    [
      'nuxt-purgecss',
      {
        paths: [
          'node_modules/@nuxtjs/vuetify/**/*.ts',
          'node_modules/@nuxt/vue-app/template/**/*.html',
          'node_modules/@nuxt/vue-app/template/**/*.vue',
        ],
        whitelist: ['v-application', 'v-application--wrap'],
        whitelistPatterns: () => [
          /^v-((?!application).)*$/,
          /^\.theme--light*/,
          /.*-transition/,
        ],
        whitelistPatternsChildren: [/^v-((?!application).)*$/, /^theme--*/],
      },
    ],
  ],
  build: {
    analyze: true,
    extractCSS: true,
    postcss: {
      plugins: {
        '@fullhuman/postcss-purgecss': {
          content: [
            'components/**/*.vue',
            'layouts/**/*.vue',
            'pages/**/*.vue',
            'plugins/**/*.js',
            'node_modules/vuetify/src/**/*.ts',
          ],
          styleExtensions: ['.css'],
          safelist: {
            standard: ['body', 'html', 'nuxt-progress'],
            deep: [
              /page-enter/,
              /page-leave/,
              /dialog-transition/,
              /tab-transition/,
              /tab-reversetransition/,
            ],
          },
        },
        'css-byebye': {
          rulesToRemove: [/.*\.v-application--is-rtl.*/, /.*\.theme--dark.*/],
        },
      },
    },
  },

parsed All (532.1 KB), gzipped All (162.71 KB)

@patrickxchong thank you for the guidance here. I went ahead and wrote a script to analyze all the vuetify helper classes our application uses and further purge the ones that aren’t. Things like mt-md-4 and order-xl-8.

// nuxt.config.js
const rules = require('./purgeUnusedCss.js')
...
"css-byebye": {
    rulesToRemove: [
      /.*\.v-application--is-rtl.*/,
      /.*\.theme--dark.*/,
      ...rules
    ]
  }

@stephenjason89 I decided to revisit how I used PurgeCSS and changed things up a little. I used @fullhuman/postcss-purgecss as a PostCSS plugin on Nuxt and combined it with another PostCSS plugin (css-byebye) and got the CSS file size down by a LOT without requiring too much manual whitelisting labor (you do have manually whitelist the transition classes used).

Attached is the Nuxt build configuration that I used, you can see the full configuration in the demo here: https://github.com/patrickxchong/nuxt-vuetify-purgecss

 /*
  ** Build configuration
  */
  build: {
    extractCSS: true,

    postcss: {
      plugins: {
          "@fullhuman/postcss-purgecss": {
            content: [
              'components/**/*.vue',
              'layouts/**/*.vue',
              'pages/**/*.vue',
              'plugins/**/*.js',
              'node_modules/vuetify/src/**/*.ts',
            ],
            styleExtensions: ['.css'],
            safelist: {
              standard: ["body", "html", "nuxt-progress"],
              deep: [
                /page-enter/,
                /page-leave/,
                /dialog-transition/,
                /tab-transition/,
                /tab-reversetransition/
              ]
            }

          },
          "css-byebye": {
            rulesToRemove: [
              /.*\.v-application--is-rtl.*/,
              /.*\.theme--dark.*/
            ]
          }
        }
    },
    /*
    ** You can extend webpack config here
    */
    extend(config, ctx) {
    }
  }

@lukakoczorowski what file is that?

I’ve just tried adding nuxt-purgecss to a Nuxt + Vuetify application and everything broke as all Vuetify CSS classes got purged.

@stephenjason89 Thanks for the code snippets. One way of fixing this would be to whitelist all col-* classes in PurgeCSS (see here: https://github.com/patrickxchong/nuxt-vuetify-purgecss/blob/master/nuxt.config.js#L73). You can see your search card working at: https://nuxt-vuetify-purgecss.now.sh/foodat.

I personally DO NOT recommend this, as by whitelisting all col-* classes, vuetify ships a lot of its unnecessary layout classes in the css bundle (you can compare the bundle sizes on your end after whitelisting col-*). I would recommend that you not use v-row and v-col for layout and instead write your own layouts with CSS Grid and Flexbox.

whitelistPatterns: [/(nav)./, /(btn)./]

@constantm Your solution and pattern worked for me like a charm. I am using Laravel-Mix, which is a wrapper around webpack. For anyone with my setup, this was how I got the path: path.join(__dirname, 'node_modules/bootstrap-vue/src/**/**/*.js') While my whitelist pattern looks like this: whitelistPatterns: [/(nav).*/,/(bg).*/, /(btn).*/],

@stephenjason89 Thanks for the code snippets. One way of fixing this would be to whitelist all col-* classes in PurgeCSS (see here: https://github.com/patrickxchong/nuxt-vuetify-purgecss/blob/master/nuxt.config.js#L73). You can see your search card working at: https://nuxt-vuetify-purgecss.now.sh/foodat.

I personally DO NOT recommend this, as by whitelisting all col-* classes, vuetify ships a lot of its unnecessary layout classes in the css bundle (you can compare the bundle sizes on your end after whitelisting col-*). I would recommend that you not use v-row and v-col for layout and instead write your own layouts with CSS Grid and Flexbox.

I second this. I was losing my mind trying to whitelist v-layout elements (v-col etc.) which even when whitelisted didn’t behave as expected on prod build, so I chose to go with CSS grid layouts. Ended up with 19.kb index.html vs 350kb when not using purgecss / postcss.

@patrickxchong Thank you for the help using /col-*/ made the bundle size 62.9kb from 61.5kb which is still a big improvement from the original 372kb. It fixed most of my layout issue. 😃 You are a lifesaver!

I was trying to get vuetify and purgeCSS to play nicely together first, I’ve not tried adding tailwind to the mix (I’m using tailwind for another project and I did get the recent update that integrates purgeCSS as well).

I did realize that one reason why it wasn’t working was because I copied the line posted by @sovofeel initially but it was using PurgeCSS instead of purgeCSS, that’s why it wasn’t working (no offense @sovofeel intended, just wanted to make sure that people are aware of the typo).

I fixed the typo and the whitelist is working pretty well now, but it’s still a little wonky (there are some things off with the UI) so I’ll give an update here once I figure that out.

my nuxt config with vuetify 2

buildModules: [ '@nuxtjs/vuetify', [ 'nuxt-purgecss', { paths: ['/node_modules/vuetify/src/**/*.ts'], }, ], ], PurgeCSS: { whitelist: ['v-application', 'v-application--wrap'], whitelistPatterns: [/^v-((?!application).)*$/, /^theme--*/, /.*-transition/], whitelistPatternsChildren: [/^v-((?!application).)*$/, /^theme--*/], },

@lobo-tuerto that is vue.config.js If you want to use it in nuxt.config.js, add the same directories to the paths module option.

This works for Vuetify ^2.1.0

module.exports = { "transpileDependencies": [ "vuetify" ], configureWebpack: { plugins: [ new PurgecssPlugin({ paths: glob.sync([ path.join(__dirname, './public/index.html'), path.join(__dirname, './**/*.vue'), path.join(__dirname, './src/**/*.js'), path.join(__dirname, './node_modules/vuetify/src/**/*.ts'), ]) }) ] } }

FYI, if anybody wants to get this working with Vue CLI 3, CSS modules and Bootstrap Vue, you can add the plugin to vue.config.js:

const glob = require("glob-all");
const path = require("path");

module.exports = {
  configureWebpack: {
    plugins: [
      new PurgecssPlugin({
        whitelistPatterns: [/^_/], // Don't remove classes with underscore prefix
        paths: glob.sync([
          path.join(__dirname, "/src/**/*.vue"),
          path.join(__dirname, "/src/**/*.js")
        ])
      }),
      new CompressionPlugin()
    ]
  },
  css: {
    loaderOptions: {
      css: {
        localIdentName: "_[name]-[hash]" // Prefix CSS modules with _ so we can whilelist them
      }
    }
  }
};

For Bootstrap Vue, you need to a add more whitelist items for dynamically generated classes that are not present in the Bootstrap Vue source files. An examples of this is button variants, eg. variant="dark" dynamically generates .btn-dark. The same applies to some responsive classes where you specify the breakpoint on the component as a prop, eg. toggleable="sm", which dynamically generates classes like .navbar-expand-md. To get around this, you can whitelist generated classes with whiteListPattern, eg:

whitelistPatterns: [/(nav).*/, /(btn).*/]