This module is not yet compatible with Nuxt 3
Head over to v2.nuxt.com

@luxdamore/nuxt-prune-html

Nuxt module to prune html before sending it to the browser for boosting performance.

🔌⚡ Nuxt Prune HTML

Code QualityDownloadsDependenciesCircle CIVersionDonate

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 📈.

Inspired by this rcfs and this issue.

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.:

Pros.:

  • Some of these features aren't "used by" a bot/audit, so you don't really need them:
  • Images with lazy-load can be fixed with a native attribute, with a custom script or with classesSelectorsToKeep (check the configuration);
  • Hydration decrease performance, so it's ok to prune it for bots 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

Lighthouse Audit beforeLighthouse Audit after


Setup

  1. Install @luxdamore/nuxt-prune-html as a dependency:
    • yarn add @luxdamore/nuxt-prune-html;
    • or, npm install --save @luxdamore/nuxt-prune-html;
  2. Append @luxdamore/nuxt-prune-html to the modules array of your nuxt.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;
      • auditUserAgent or matchUserAgent, trigger .match() method;
  • query-parameters: prune based on one or more query parameter, tests key / value based on queryParametersToPrune / queryParametersToExcludePrune:
    • you can also specify routes in nuxt.config, ex. { generate: { routes: [ '/?prune=true' ] } } )
  • headers-exist: prune based on one or more header, tests key / value based on headersToPrune / headersToExcludePrune.

N.B.: It's possibile to mix different types.


  • Nuxt hooks, the plugin has access to request.headers only if the project is running as a server (ex. nuxt start)
    • If you generate your site it's not possibile to check request.headers, so (for types: [ 'default-detect', 'headers-exist' ]) it always prune, but You can disable this behavior by setting hookGeneratePage to false (or by using the type query-parameters);
  • Usage with types: [ 'default-detect' ], load the MobileDetect library;
  • It use Cheerio, jQuery for servers, library to filter and prune the html.

Advices


👩🏻‍💻👨🏻‍💻 Development

  1. Clone the repository:
    • git clone https://github.com/LuXDAmore/nuxt-prune-html.git;
  2. Install dependencies:
    • yarn install (or npm install);
  3. Start a development server:
    • yarn dev (or npm run dev);
  4. Extra, generate the documentation (Github Pages):
    • yarn generate (or npm run generate);
    • the content is automatically generated into the /docs folder.

🐞 Issues

Please make sure to read the issue reporting checklist before opening an issue. Issues not conforming to the guidelines may be closed immediately.

👥 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-present Luca Iaconelli

💼 Hire me

Contacts

💸 Are you feeling generous today?

If You want to share a beer, we can be really good friends 😄

Paypal // Patreon // Ko-fi

It's always a good day to be magnanimous - cit.