ui
ui copied to clipboard
Reduce Tailwind CSS bundle size for unused components
Related to #802 do you want separate issues on reducing the unused colors (this could be an option parameter, even) and reducing bundle size for unused components?
Removing unused colors is already done through Smart safelisting and I already answered here https://github.com/nuxt/ui/issues/802#issuecomment-1759280846 on how to select only desired colors, not sure why you want to create another issue.
I read the docs and that's what nuxt/ui
is trying to achieve. But it doesn't seem to work as intended so I created a bug: #889. We can continue this color discussion there and leave the ticket for the unused component thing.
Sorry to jump in, but simply adding @nuxt/ui
to an empty project creates an inline css of 243kb.
Is that related to this issue or something that I'm missing from the documentation?
It is related to this issue, the whole config is taken into account by Tailwind: https://github.com/nuxt/ui/blob/dev/src/runtime/ui.config.ts
Is that ui.config file now available for Tailwind to do it's magic? Or does it do something more in nuxt/ui code? I am trying to scope the fix here. If nuxt/ui would know which components the dev has used in the code it could cherry-pick those definitions from the config file for Tailwind, right?
Hello,
This is pretty serious affecting performance.
@Atinux can this pushed forward please?
Thanks!
The build size is huge when using Static Site. Weirdly, NuxtUI is adding the tailwind styles inside a
@benjamincanac any timeline for this?
It's labeled as enhancement but I'm sure for many this issue is a deal-breaker for using @nuxt/ui
Hey! We're researching into this and hoping to come up with a breakthrough soon. 😄
The build size is huge when using Static Site. Weirdly, NuxtUI is adding the tailwind styles inside a
@sigmaxf I think this is more of nuxt's features. Try to fiddle with https://nuxt.com/docs/guide/going-further/experimental-features#inlinessrstyles
There should be a workaround available for this now, if someone would like to report back on this please.
See "Tailwind CSS bundle" section on release notes for https://github.com/nuxt/ui/releases/tag/v2.11.0.
@ineshbose thanks! your help is highly appreciated!
I've tried excluding everything (on an empty project), and went down from 253kb to 185kb. Big improvement but still huge for production.
Just to understand the issue, is it not possible to load nuxt ui before cssnano?
Here's what I excluded, if it helps anyone:
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/data/table.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/elements/accordion.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/elements/alert.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/elements/avatar.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/elements/avatarGroup.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/elements/badge.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/elements/button.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/elements/buttonGroup.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/elements/chip.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/elements/dropdown.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/elements/kbd.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/elements/meter.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/elements/meterGroup.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/elements/progress.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/forms/checkbox.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/forms/radioGroup.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/forms/radio.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/forms/toggle.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/forms/formGroup.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/forms/select.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/forms/textarea.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/forms/selectMenu.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/forms/range.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/forms/input.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/layout/card.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/layout/container.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/layout/divider.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/layout/skeleton.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/navigation/breadcrumb.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/navigation/commandPalette.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/navigation/pagination.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/navigation/verticalNavigation.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/overlays/contextMenu.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/overlays/modal.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/overlays/notification.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/overlays/notifications.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/overlays/popover.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/overlays/slideover.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/overlays/tooltip.mjs')
@ronenteva have you limited the amount of color entries in the final outcome? It has an impact, too. See #889
@jrutila thanks! it's now down to 102kb
import colors from 'tailwindcss/colors';
import {resolve} from 'pathe';
export default {
theme: {
colors: {
transparent: 'transparent',
current: 'currentColor',
black: colors.black,
white: colors.white,
gray: colors.gray,
red: colors.red,
green: colors.green
}
},
content: {
files: [
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/data/table.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/elements/accordion.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/elements/alert.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/elements/avatar.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/elements/avatarGroup.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/elements/badge.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/elements/button.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/elements/buttonGroup.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/elements/chip.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/elements/dropdown.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/elements/kbd.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/elements/meter.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/elements/meterGroup.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/elements/progress.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/forms/checkbox.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/forms/radioGroup.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/forms/radio.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/forms/toggle.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/forms/formGroup.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/forms/select.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/forms/textarea.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/forms/selectMenu.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/forms/range.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/forms/input.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/layout/card.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/layout/container.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/layout/divider.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/layout/skeleton.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/navigation/breadcrumb.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/navigation/commandPalette.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/navigation/pagination.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/navigation/verticalNavigation.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/overlays/contextMenu.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/overlays/modal.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/overlays/notification.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/overlays/notifications.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/overlays/popover.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/overlays/slideover.mjs'),
'!' + resolve('node_modules/@nuxt/ui/dist/runtime/ui.config/overlays/tooltip.mjs')
]
}
};
Just to understand the issue, is it not possible to load nuxt ui before cssnano?
cssnano should include all the styles from NuxtUI after build.
Let me add another solution to this problem: @nuxtjs/critters
This should remove unused styles from your page, but few things to note - load this module BEFORE @nuxt/ui
, and it only works for generate/prerendered pages.
@ineshbose Will the critters solution work for ssr:false
and npm run generate
project?
Will the critters solution work for
ssr:false
andnpm run generate
project?
It needs SSR enabled. Rest, you can exclude files from Tailwind content (a for-loop or even a Nuxt module would be quite elegant) for now.
@benjamincanac @ineshbose
I'm sorry to bump the issue but I want to make sure you guys understand that anyone who relies on SEO can't afford using @nuxt/ui
until this is fully resolved.
@ineshbose
I'm sorry to bump the issue but I want to make sure you guys understand that anyone who relies on SEO can't afford using
@nuxt/ui
until this is fully resolved.
Hey! Thanks for pinging - this issue is still in mind. Unfortunately content detection is a tricky part of Tailwind JIT and a component library based on Tailwind would have such limitations. We'll have to come up with some ground breaking stuff to have dynamic content based on import (contributions welcome) which all of TW community would benefit from, but till then we have a workaround available.
Let me add another solution to this problem:
@nuxtjs/critters
This should remove unused styles from your page, but few things to note - load this module BEFORE
@nuxt/ui
, and it only works for generate/prerendered pages.
Do you mind sharing an example of how to use critters to remove the unused styles? I couldn't get it to make any difference.
Another workaround is to use nuxt-purgecss and safelist all classes in the components in use.
@ronenteva Excluding all of the config files did not do the trick?
@benjamincanac I stopped using it at some point but I honestly can't remember what wasn't working. I'll give it another try and let you know.
Update:
I tested again, adding @nuxt/ui
to a clean project and excluding all components, still adds 114kb of inline style.
If dark mode is not needed, you can save another 30k by setting:
nuxt.config.js
colorMode: {
preference: 'light'
}
tailwind.config.js
darkMode: []
Providing a workaround:
- first, build you app and launch it.
- curl http://localhost:3000 (for example)
- copy the longest
- save it to a seperate css file and upload it to cdn
- in nuxt.config.js/ts, set tailwindcss:{cssPath:false}, and in app:{head:{link:{... import the css on cdn
significantly drop from ~700kb to ~160kb
(the 160kb is because another ui library I use.
Any updates on this? Or is there a way for nuxt ui to only bundle tailwind classes from used nuxt ui components? This defeats the purpose of tailwind.
Any updates on this? Or is there a way for nuxt ui to only bundle tailwind classes from used nuxt ui components? This defeats the purpose of tailwind.
nope.
Any updates on this? Or is there a way for nuxt ui to only bundle tailwind classes from used nuxt ui components? This defeats the purpose of tailwind.
The key problem is that even if you only import the tailwind classes used, it's still a lot of classes considering how many tailwind classes are used in nuxt-ui. There is just no way around.
- SSR needs the css embed in the webpage itself rather than importing another css file.
- NuxtUI uses a bunch of tailwind classes.
I just gave up. There is little difference between 70kb and 700kb nowadays.