Speed up Nuxt Builds on Netlify
Tags: netlify, nuxt, showdev, javascript
Published: 2020-10-25 12:32:09

---
title: "Speed up Nuxt Builds on Netlify"
published: true
description: If you're not careful, your build times for Nuxt can spiral out of control. This is my journey to get from 30 minutes to 1 minute builds.
tags: netlify, nuxt, showdev, javascript
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/1ir2fc34lsy2bw48rap4.png
series: "WordPress to Nuxt"
---

If you're not careful, your build times for [Nuxt](https://nuxtjs.org/) can spiral out of control. After enabling [Nuxt i18n](https://i18n.nuxtjs.org/), we reached 27-minute production deploys.

Once we had added a load of nice-to-have Netlify plugins, our deploy times went up again. Before I knew it, our deploys on Netlify were failing, timing out at 30 minutes.

This post addresses build time problems for sites using version `2.14` of [Nuxt](https://nuxtjs.org/). `2.14` introduces [full-static](https://nuxtjs.org/blog/going-full-static/#crawler-integrated) builds and includes all the lovely new crawler changes.

> ***Note***: I will refer to the time it takes for Nuxt to run `generate` as build times. Deploy times are the time it takes Netlify to run `generate` and then publish it.

For this post, I'll be using the globally recognised "turd" scale for measuring satisfaction–💩💩💩 to 😍🎉🔥.

💩💩💩 30-minute deploys (timeout)

## Unnecessary Content

Unnecessary content was an issue for us. Having imported nearly 600 articles from our legacy WordPress site, we were building pages for every category, tag, and author too. This lead to some 17500 physical pages being rendered by the Nuxt full static build. After reviewing the metadata on our posts, I managed to reduce our build to just over 3000 pages...

Our Netlify build jumped from 30-minute timeouts to 15 minute deploys.

👎🏻👎🏻👎🏻 15-minute deploys

![Screenshot of 15-minute deploy in Netlify](https://dev-to-uploads.s3.amazonaws.com/i/wy7lz0u9v9mgchu7ka3p.png)

## Skip Optional Dependencies

While not Nuxt specific, only installing the dependencies you need can speed up the Netlify install before the build even starts.

[The `--no-optional` argument](https://docs.npmjs.com/cli/install) will prevent optional dependencies from being installed by `npm`. [There is a yarn equivalent](https://classic.yarnpkg.com/en/docs/cli/install/#toc-yarn-install-ignore-optional).

You can add this to Netlify in the config, or add it to the [environment variables](https://docs.netlify.com/configure-builds/environment-variables/) on the dashboard.

```toml
# netlify.toml

[build.environment] 
  NPM_FLAGS = "--no-optional"
```

This did nothing for our deploy time, but it may help others. 🤪

👎🏻👎🏻👎🏻 15-minute deploys 

## Code Minification

Nuxt has strong [default HTML minification settings](https://github.com/nuxt/nuxt.js/blob/5fa768373da1adfd8c76145b2ec95b7824af93b4/packages/config/src/config/build.js#L89-L101) used for post-processing builds.

```js
  // ...

  html: {
    minify: {
      collapseBooleanAttributes: true,
      decodeEntities: true,
      minifyCSS: true,
      minifyJS: true,
      processConditionalComments: true,
      removeEmptyAttributes: true,
      removeRedundantAttributes: true,
      trimCustomFragments: true,
      useShortDoctype: true
    }
  },

  // ...
```

Nuxt already minifies CSS and JS using WebPack plugins. So we can disable inline CSS and JS minification.

```js
  // ...

  build: {
    html: {
      minify: {
        minifyCSS: false,
        minifyJS: false,
      }
    }
  }

  // ...
```

When I read about this, it suggested we'd see a 10x improvement on the initial build. In reality, we saw a minute or two in reductions.

👎🏻👎🏻👍🏻 13-minute deploys

## Turn Off Logging

Even locally, the verbose logging of several thousand lines in the terminal can slow EVERYTHING down. Most of the logging is formatted from Nuxt, to.

Disable logging anything but errors with the `CI` environment variable.

You can add this to Netlify in the config, or add it to the [environment variables](https://docs.netlify.com/configure-builds/environment-variables/) on the dashboard.

```toml
# netlify.toml

[build.environment]
  CI = "1"
```

This made a surprising difference, slicing a fair chunk off our build time.

👎🏻👍🏻👍🏻 8-minute deploys

## Post Processing 

If you've played with Netlify deployment configurations, you'll know there is a wealth of options now.

![All the Netlify deploy post-processing options](https://dev-to-uploads.s3.amazonaws.com/i/3kavvlk488vuzibl1wad.png)

I had lots of options ticked, and we've already established that Nuxt does most of it already.

Reasons to turn them ***all*** of—for us at least:

- We already have a plan to generate our images based on rules we've established for media creation already. We can do the optimisation steps and CDN uploads here in the future.
- Nuxt does minification of HTML, JS and CSS already.
- The Nuxt static build does-Pre-rendering.

Tick, tick, tick. All off.

👍🏻👍🏻👍🏻 5-minute deploys

![Screenshot of 5-minute deploy in Netlify](https://dev-to-uploads.s3.amazonaws.com/i/iq4jnar33uladjhxz5ih.png)

## Real Talk

Getting our deploys much quicker [without paying for enterprise Netlify](https://www.netlify.com/blog/2020/03/25/announcing-faster-deploys-for-large-sites/) is unrealistic. I have a ton of optimisation to do on my Vue components, but I don't expect to see *much* more time saved.

***It wasn't actually the production build I was so keen to reduce the time on***, it was the previews as we've gone FULL NETLIFY and adopted Netlify CMS for git-based content storage. So, every time we edit a post in Netlify CMS, it creates a pull-request for the edited file. At 30 minute builds, with a team of 8 people working on content, well you see where this is going—lots of waiting for builds. Less now, at least.

## BUT WAIT, There's More 😮

Is there a way to reduce our preview builds? That was the original motivation to reduce build times in the first place!

After a brief Googlin', I came across [issue #6138 raised on the Nuxt project](https://github.com/nuxt/nuxt.js/issues/6138), on how to generate a single route.

In the latest version of Nuxt, the solutions in the GitHub issue didn't actually work.

##### But, it did give me an idea.

Since Nuxt `2.14`, we've had the crawler to discover pages. And, if I want to provide additional routes, I can use the [`routes()`](https://nuxtjs.org/api/configuration-generate/#routes) property of the [generator](https://nuxtjs.org/api/configuration-generate/#routes) config.

So, I thought to myself, "can I turn off the crawler and provide it a single route, somehow"?

##### The answer was yes.

Casually breaking my `nuxt.config.js`...

```js
  generate: {
    crawler: false,
    routes() {
      return ["/blog/a-test-blog-post-made-in-netlify-cms"]
    }
  }
```

Doing this resulted in almost immediate build times, so once tested on Netlify I was down to about *1 minute deploys*. It was just building the physical routes (everything in your `/pages` directory) without crawling for any dynamic routes. Our physical routes make up less of our site than was worth worrying about.

Could I make this context-driven based on the preview build? 

##### Well, it hit me like a slap around the face.

The slug for the new post created in Netlify CMS was part of the branch name.

`cms/blog/a-test-blog-post-made-in-netlify-cms`

And, the branch name was available on our preview build as the environment variable, `HEAD`.

```
# console.log(process.env.HEAD)
cms/blog/a-test-blog-post-made-in-netlify-cms
```

##### I'm getting closer.

A quick explore of the other environment variables provided in preview builds, I noticed `PULL_REQUEST` is an indicator whether the build is from a pull/merge request (`true`) or not (`false`).

So here was the rough code I put together to make use of this.  Add a new function to the top of the `nuxt.config.js` file.

```js
// nuxt.config.js

// ...

const isPreviewBuild = () => {
  return process.env.PULL_REQUEST && process.env.HEAD.startsWith('cms/')
}

// ...

module.exports = {
// etc...
```

So this returns whether its a PR ***and*** if the branch name begins with `cms/` (generated by Netlify CMS).

How can we use this? I'm glad you asked. Edit the "generate" property in `nuxt.config.js`.

```js
// nuxt.config.js

// ...

module.exports = {
  // ...

  generate: {
    crawler: !isPreviewBuild(),
    routes() {
      return isPreviewBuild() ? ["/blog/a-test-blog-post-made-in-netlify-cms"] : []
    }
  }
}
```

Tested? Works, still my ~1 minute deploy! Now, route from the branch name. Another new function to `nuxt.config.js`.

```js
// nuxt.config.js

// ...

const previewRoute = () => {
  const [, type, slug] = process.env.HEAD.split('/')

  return [ `/${type}/${slug}` ]
}

// ...

module.exports = {
// etc...
```

The `type` is super important because it means we can also preview `video` and `author` pages as well `blog`. Really cool side effect. Added with one last edit to the `nuxt.config.js` file.

```js
// nuxt.config.js

// ...

module.exports = {
  // ...

  generate: {
    crawler: !isPreviewBuild(),
    routes() {
      return isPreviewBuild() ? previewRoute() : []
    }
  }
}
```

😍🎉🔥 1-minute deploys

![Screenshot of 1-minute deploy in Netlify](https://dev-to-uploads.s3.amazonaws.com/i/muecyflobomroqw6rlnv.png)

## Conclusion

> Confession: I built most of the final code without testing it, I used the `BRANCH` environment variable thinking it would be the branch name. It is not. So when it didn't work, I thought I'd gone down a big old rabbit hole building this. I thought I'd wasted hours. Nope, got the environment variable wrong. Read the docs, folks.

It's been almost 5 years since I was in a role that had me building code that would be relied upon in a production environment. I shouldn't be surprised (but I am) that the off-the-shelf configuration for Nuxt isn't optimised for production builds.

I'm generally cautious of introducing environment-aware code into an application, but I have ignored my better judgement as this is environment-aware-configuration as code. You should be very careful about introducing code that fundamentally changes how an application runs or is built based on the environment it is running in.

- Know your platform.
- Read the flipping manual.
- Do all the Googlin'.
- Take care with environment-aware code.

See exactly how we're using this on our GitHub repository.

{% github Nexmo/deved-platform no-readme %}

## Credits

- [issue #6138 raised on the Nuxt project](https://github.com/nuxt/nuxt.js/issues/6138) 👏
- [10x Faster Nuxt Builds on Netlify](https://www.voorhoede.nl/en/blog/10x-faster-nuxt-builds-on-netlify) ❤️
- Decent help from the [Nuxt Discord](https://discord.nuxtjs.org/) community 🔥