πβ‘ Nuxt Prune HTML
Nuxt module to prune html before sending it to the browser (it removes elements matching CSS selector(s)), useful for boosting performance showing a different HTML for bots/audits by removing all the scripts with dynamic rendering.
π Motivation
Due to the versatility of Nuxt (and of the SSR in general), a website generated (or served) via node server, has everything it needs already injected in the HTML (ex. css styles). So, usually, for a bot, a audit or for a human, the website its almost visually the same with or without Javascript.
This library is born to remove all the scripts injected into the HTML only if a visitor is a Bot or a Performance Audit (ex. a Lighthouse Audit). This should speed up (blazing fast) your nuxt-website up to a value of ~99 in performance because it cheats various scenarios.
Usually, with less assets, resources and html to download, the number of urls crawled by a bot are widely boosted π.
Features
- Prune based on default detection;
- match the user-agent;
- match a bot;
- match an audit;
- match a custom-header;
- Prune based on headers values (useful in/for Lambdas);
- Prune based on query parameters (useful during navigation, hybrid-experience).
Pro et contra
This could cause some unexpected behaviors, but..
Cons.:
- No
SPA routingonclient-sidefor bots and audits; - No
hydrationonclient-sidefor bots and audits:- ex.
vue-lazy-hydrationneed Javascript client-side code to trigger hydrateOnInteraction, hydrateWhenIdle or hydrateWhenVisible;
- ex.
- No
<client-only>components; - Can break
lazy-loadfor images.
Pros.:
- Some of these features arenβt βused byβ a bot/audit, so you donβt really need them:
- bots doesnβt handle
SPA routing; <client-only>components could lead in a slower TTI;<client-only>components can contain a static placeholder;
- bots doesnβt handle
- Images with
lazy-loadcan be fixed with a native attribute, with a customscriptor withclassesSelectorsToKeep(check the configuration); - You can pre-load ad inject all of the data that you need (Rest API, GraphQL, ecc) during the build phase with nuxt-apis-to-file, speeding up the website loading time;
Hydrationdecrease performance, so itβs ok to prune it forbots or audits;- Less HTML, assets and resources are served to browsers and clients;
- Bot/audit only have the Javascript they need;
- With less assets to download, the number of urls crawled are widely boosted;
- Bots, PageSpeed Insights, Google Measure and Lighthouse Audit are already pruned by the plugin with the default configuration;
- Faster web-vitals, faster TTI, faster FCP, faster FMP, faster all.
N.B.: This is known as Dynamic Rendering and itβs not considered black-hat or cloaking.
π‘ Lighthouse

Setup
- Install
@luxdamore/nuxt-prune-htmlas a dependency:yarn add @luxdamore/nuxt-prune-html;- or,
npm install --save @luxdamore/nuxt-prune-html;
- Append
@luxdamore/nuxt-prune-htmlto themodulesarray of yournuxt.config.js.
Configuration
// nuxt.config.js
export default {
// Module - installation
modules: [ '@luxdamore/nuxt-prune-html' ],
// Module - default config
pruneHtml: {
enabled: false, // `true` in production
hideGenericMessagesInConsole: false, // `false` in production
hideErrorsInConsole: false, // deactivate the `console.error` method
hookRenderRoute: true, // activate `hook:render:route`
hookGeneratePage: true, // activate `hook:generate:page`
selectors: [
// CSS selectors to prune
'link[rel="preload"][as="script"]',
'script:not([type="application/ld+json"])',
],
classesSelectorsToKeep: [], // disallow pruning of scripts with this classes, n.b.: each `classesSelectorsToKeep` is appended to every `selectors`, ex.: `link[rel="preload"][as="script"]:not(__classesSelectorsToKeep__)`
link: [], // inject custom links, only if pruned
script: [], // inject custom scripts, only if pruned
htmlElementClass: null, // a string added as a class to the <html> element if pruned
cheerio: {
// the config passed to the `cheerio.load(__config__)` method
xmlMode: false,
},
types: [
// it's possibile to add different rules for pruning
'default-detect',
],
// ππ» Type: `default-detect`
headerNameForDefaultDetection: 'user-agent', // The `header-key` base for `MobileDetection`, usage `request.headers[ headerNameForDefaultDetection ]`
auditUserAgent: 'lighthouse', // prune if `request.header[ headerNameForDefaultDetection ]` match, could be a string or an array of strings
isAudit: true, // remove selectors if match with `auditUserAgent`
isBot: true, // remove selectors if is a bot
ignoreBotOrAudit: false, // remove selectors in any case, not depending on Bot or Audit
matchUserAgent: null, // prune if `request.header[ headerNameForDefaultDetection ]` match, could be a string or an array of strings
// ππ» Type: 'query-parameters'
queryParametersToPrune: [
// array of objects (key-value)
// trigger the pruning if 'query-parameters' is present in `types` and at least one value is matched, ex. `/?prune=true`
{
key: 'prune',
value: 'true',
},
],
queryParametersToExcludePrune: [], // same as `queryParametersToPrune`, exclude the pruning if 'query-parameters' is present in `types` and at least one value is matched, this priority is over than `queryParametersToPrune`
// ππ» Type: 'headers-exist'
headersToPrune: [], // same as `queryParametersToPrune`, but it checks `request.headers`
headersToExcludePrune: [], // same as `queryParamToExcludePrune`, but it checks `request.headers`, this priority is over than `headersToPrune`
// Emitted events for callbacks methods
onBeforePrune: null, // ({ result, [ req, res ] }) => {}, `req` and `res` are not available on `nuxt generate`
onAfterPrune: null, // ({ result, [ req, res ] }) => {}, `req` and `res` are not available on `nuxt generate`
},
};
With link and script itβs possibile to add one or more objects on the pruned HTML, ex.:
export default {
pruneHtml: {
link: [
{
rel: 'preload',
as: 'script',
href: '/my-custom-lazy-load-for-bots.js',
position: 'phead', // Default value is 'body', other allowed values are: 'phead', 'head' and 'pbody'
},
{
rel: 'stylesheet',
href: '/my-custom-styles-for-bots.css',
position: 'head',
},
],
script: [
{
src: '/my-custom-lazy-load-for-bots.js',
lazy: true,
defer: true,
},
],
},
};
N.B.: the config is only shallow merged, not deep merged.
Types / Rules
Possible values are [ 'default-detect', 'query-parameters', 'headers-exist' ]:
default-detect: prune based on one header(request.headers[ headerNameForDefaultDetection ])- different checks with MobileDetect:
isBot, trigger.is( 'bot' )method;auditUserAgentormatchUserAgent, trigger.match()method;
- different checks with MobileDetect:
query-parameters: prune based on one or more query parameter, testskey / valuebased onqueryParametersToPrune / queryParametersToExcludePrune:- you can also specify routes in
nuxt.config, ex.{ generate: { routes: [ '/?prune=true' ] } }
- you can also specify routes in
headers-exist: prune based on one or more header, testskey / valuebased onheadersToPrune / headersToExcludePrune.
N.B.: Itβs possibile to mix different types.
Related things you should know
- Nuxt hooks, the plugin has access to
request.headersonly if the project is running as a server (ex.nuxt start)- If you
generateyour site itβs not possibile to check request.headers, so (fortypes: [ 'default-detect', 'headers-exist' ]) it always prune, but You can disable this behavior by settinghookGeneratePagetofalse(or by using the typequery-parameters);
- If you
- Usage with
types: [ 'default-detect' ], load the MobileDetect library; - It use Cheerio, jQuery for servers, library to filter and prune the html.
Advices
- Before setting up the module, try to Disable JavaScript With Chrome DevTools while navigate your website, this is how your website appear (when nuxt-prune-html is enabled);
- For
<client-only>components you should prepare a version that is visually the same with the placeholder slot; - You can check the website as a GoogleBot, following this guide;
- The nuxt-apis-to-file module can help you with data payload extraction during the build time.
π©π»βπ»π¨π»βπ» Development
- Clone the repository:
git clone https://github.com/LuXDAmore/nuxt-prune-html.git;
- Install dependencies:
yarn install(ornpm install);
- Start a development server:
yarn dev(ornpm run dev);
- Test your code:
yarn test(ornpm run test);
- Extra, generate the documentation (Github Pages):
yarn generate(ornpm run generate);- the content is automatically generated into the
/docsfolder.
π Issues
Please make sure to read the issue reporting checklist before opening an issue. Issues not conforming to the guidelines may be closed immediately.
π Discussions
Weβre using Github discussions as a place to connect with other members of our community. You are free to ask questions and share ideas, so enjoy yourself.
π₯ Contribution
Please make sure to read the contributing guide before making a pull request.
π Changelog
Details changes for each release are documented in the release notes.
π License
MIT License // Copyright (Β©) 2019-now Luca Iaconelli
πΌ Hire me
πΈ Are you feeling generous today?
If You want to share a beer, we can be really good friends π
β Itβs always a good day to be magnanimous - cit.