You are looking for short loading times and high interactivity for your website? Nuxt got you covered: A JavaScript framework that serves a pre-rendered page for a fast first meaningful paint and then transforms the page into a full interactive single page application.
Nuxt and Universal Web Applications ¶
These kinds of websites are “universal” applications, as JavaScript code runs both on the server and on the client. If you don’t want to run JavaScript on the server in production, you can pre-render the pages and serve them via a content delivery network (CDN). This helps you to scale your website even better, so this is what I’m using here. Use Google’s Lighthouse to find out how your site performs.
Nuxt comes with support for Vue.js for components and Vuex for state handling. Webpack provides the build automation. If you are more inclined to use React, have a look at Next.js for a similar functionality.
To cover the basics, I recommend the Nuxt documentation for an introduction and a getting started. The following chapters assume that you are familiar with the essentials of npm, Nuxt and JavaScript. It requires no special knowledge about Vue.js. All configurations worked with Nuxt 2.3.4 and Chrome 71 (Update: they still work in Nuxt 2.8.0 and Chrome 74).
Making Nuxt faster ¶
When you use Nuxt out of the box, it is already quite fast: A single HTTP request downloads the pre-rendered page with all CSS at the top, followed by non-blocking JavaScript and ready-to-display HTML. The browser renders it without the need to re-layout if you haven’t used external fonts and all images have a known size.
This post helps you to optimise this further by adding preview images and reducing the CSS included in the page. An upcoming post will cover how to optimise Nuxt for search engines and social media. I will show example code from this website (my homepage). I’ll introduce helpful plugins and their real-world use and configuration. Your requirements might be different, your mileage may vary depending on what you’re trying to achieve.
Extend Nuxt with webpack plugins ¶
Showing pre-rendered HTML in the browser includes no images yet. The smaller the images are, the faster they will load. You can include preview images of a smaller resolution in the initial HTML while the user is waiting for the full image.
The following chapters show how you can add existing webpack plugins to extend Nuxt. You place all these plugins into the nuxt.config.js file. You don’t edit a webpack configuration file, but use JavaScript to change the webpack configuration.
Optimise SVG images with imagemin ¶
Even if you use small vector SVG images, they contain unnecessary comments and tags you can remove. When you automate this in your build with the image-webpack-loader, the SVGs you serve are always optimal. The original files in your repository will remain unchanged.
First you add the new plugin and save it to your package.json file. As a development dependency it runs only at build time, and will not increase the size of your app in the browser.
Then add the plugin to the build process. The following snippet shows the lines you add to your configuration. Leave all other lines unchanged.
The additional plugin configuration keeps the viewbox but removes the dimensions so Internet Explorer show the images in the correct size.
Optimise raster images with the responsive loader ¶
Images in the correct size with optimal compression save bandwidth and speed up rendering your page, but this is tedious to ensure. The solution is the responsive loader plugin instead of the original image loader. Again, the original files remain untouched. Compared to minimising SVGs this takes more computing time during your build. It re-scales the images you use and also calculates previews. You can show the preview in the pre-rendered page while the browser loads the real image from the server.
Again, you first need to install the plugin. The plugin depends on an image scaler plugin. jimp is slower, but is a 100% JavaScript implementation that will run on any platform. If you have lots of images to transform, the plugin recommends using sharp for image scaling.
Now you add the new loader to handle JPEG, GIF and PNG images, and tune the existing image loader to exclude them. For development you can skip generating previews and scaled images to speed up the build, but you cannot test the previews in development this way.
It compresses the images using the quality stated in the configuration (here: 85%). This reduces the size of the images if your original files have a higher quality.
The new loader adds resizing functionality. When you reference images in your style sheets, you can add query parameters. The plugin creates a scaled image at build time and replaces this with the filename of the resized image.
You can also load images via JavaScript and use them in your templates. The src attribute will give you the standard URL. The placeholder attribute gives you a base64 data stream to embed in your page and show it to the user while she or he waits for the real image to arrive.
You can use this plugin to provide a scaled set of images as a source set. This allows the browser to select a pre-scaled image matching the screen resolution and displayed size of the image. While adding a source set is straightforward, you must also provide the sizes attribute with the expected size of the image depending on the screen’s width. If you don’t provide this attribute, the browser takes the full width of the window as the default and loads a bigger image than necessary. The following example sets the image widths via query parameters for this specific image. You can also provide defaults in the plugin’s configuration.
The following box shows the two images side by side instead of layering them on top of each other. The preview is 1.1k (gzip’ed) and will be inlined in the pre-rendered page, the full picture is 30k and will be loaded afterwards.
Things to watch out for:
Google Chrome will prefer a cached image with a higher resolution even if the sizes attribute indicates a smaller resolution. When you test and verify in the Network tab of the developer tools that the correct images are loading, you’ll need to issue a hard-reload or clear-cache-reload command. To do this, right-click on the reload button while your developer tools are open and choose the appropriate option.
Google Chrome will also load the images even when the element is hidden. To not load the image, clear the background image when element is hidden, and use the picture element to direct images to a placeholder.
Optimise CSS with purgecss ¶
In the default configuration each pre-rendered page of Nuxt contains all the CSS your application needs. You can change this and externalise it to a file that every pre-rendered page links to, but this would cause a re-layout of the page if the browser doesn’t have a cached version of the CSS.
Once the user navigates within your Nuxt application, this is no longer an issue as Vue.js will not reload the page but only exchange the information rendered in the DOM.
When you use a CSS framework, the first page sent to the browser can become quite big. The plugin purgecss helps you to include only the parts of the CSS framework you used on the site. This will substantially reduce the size.
Purgecss can run as a webpack plugin or a postcss plugin. For Nuxt you must use it as a postcss plugin to keep the CSS inside the page. There is now also a Nuxt plugin to do this, but as the lines of code necessary are very similar, I used the webpack plugin directly.
As a developer you can choose if you want to have purgecss running in development mode:
When you make major CSS changes, you might need re-start the Nuxt development mode for purgecss to pick up the new CSS.
If you disable purgecss in development mode, you might miss a situation where it removed some CSS in the production build you wanted to keep.
I’ve settled for the first option here. If you want to try the second option, you can install nuxt-purgecss that has an automatic development.
First install the plugin:
This is my extended purgecss configuration. I’ve added additional content folders for purgecss to scan for CSS classes.
Purgecss needs some hints for CSS classes it can’t discover from the sources. It accepts these as a white list of CSS classes or as regular expressions. My white list includes the html and body tag that are present only in the generated index.html that’s not visible to purgecss. has-navbar-fixed-top is a CSS class I’ve set to the html tag using my Nuxt configuration using htmlAttrs (see below).
nuxt-progress is the progress bar when Nuxt is loading from the background, and nuxt-link-exact-active is the class added to Nuxt links when they are active. The layout and error pages use the __layout and __nuxt classes.
As I’m using font awesome, I add the svg-inline--fa classes it adds at run time.
Summary ¶
Thanks reading this post. You have seen how to
boost your user experience with preview images,
speed up your website by minimising images and CSS, and
extend Nuxt with webpack and postcss plugins.
You’ve looked at real-life configuration examples that will give you an idea for your own projects. If you have questions, contact me via a direct message on twitter or via email, I’m happy to help.
If you found this article helpful and you want to share it, please do so via the Twitter button below. If you found this article helpful and you want to share it, do so on Twitter.