Mastering Nuxt SSG: Take Full Control of Your Static Site Output

2024-08-18

Nuxt not only supports server-side rendering (SSR) but also offers powerful static site generation (SSG) features. This enables developers to easily deploy websites to any platform that supports the JAMstack architecture.

This article will explore how to generate static sites using Nuxt and provide detailed insights into controlling various behaviors and configuration options during the generation process. From pre-rendering settings to managing assets output, we'll walk through how to effectively manage static site generation with Nuxt.

Generating a Static Site

Generating a static site in Nuxt is straightforward; simply run the following command:

Terminal
nuxt generate

Controlling Pre-rendering

By default, Nuxt pre-renders each page when generating a static site, producing the corresponding HTML files.

If you prefer to generate a client-side rendered SPA without pre-rendering, you can disable this feature by setting ssr to false in your configuration.

nuxt.config.ts
export default defineNuxtConfig({
  ssr: false
})

Controlling the Output Path

Nuxt uses Nitro for pre-rendering and outputs the static site to the default .output/public directory.

If you need to change the output directory, you can customize the output path using the following configuration:

nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    output: {
      dir: resolve('./.my-output'),
      publicDir: path.join(__dirname, './.my-output/public'),
    }
  }
})

Output Directory Structure

After pre-rendering and generating the static site, Nuxt creates a structured directory by default.

In this section, we'll explain the purpose of each file and directory and explore the relevant configuration options.

πŸ“‚ _nuxt

This directory contains the site's assets, including all CSS and JavaScript files.

You can customize the name of this directory by configuring app.buildAssetsDir.

nuxt.config.ts
export default defineNuxtConfig({
  app: {
    buildAssetsDir: '/_my_assets/'
  }
})

πŸ“‚ _nuxt/builds

This directory stores files related to the App Manifest, which includes information about the latest build, such as routes and timestamps.

If you don't need these files, you can disable this feature by setting experimental.appManifest to false, preventing the directory from being generated.

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    appManifest: false
  }
})

πŸ“„ _nuxt/*.js

Nuxt uses Vite with Rollup for JavaScript bundling.

You can control the output format of JavaScript files by configuring vite.build and setting rollupOptions. Some common examples include:

  • Customizing the output file name format (output.entryFileNames, output.chunkFileNames)
    nuxt.config.ts
    export default defineNuxtConfig({
      vite: {
        build: {
          rollupOptions: {
            output: {
              entryFileNames: '_nuxt/my-[name]-[hash].js',
              chunkFileNames: '_nuxt/my-[name]-[hash].js'
            }
          }
        }
      }
    })
    
  • Merging all *.js files into a single file (output.inlineDynamicImports)
    nuxt.config.ts
    export default defineNuxtConfig({
      vite: {
        build: {
          rollupOptions: {
            output: {
              inlineDynamicImports: true
            }
          }
        }
      }
    })
    

πŸ“„ _nuxt/*.css and Other Assets

You can configure the output format for CSS and other assets by modifying the vite.build configuration. Some settings include:

Customizing the output file name format (rollupOptions.output.assetFileNames)

nuxt.config.ts
export default defineNuxtConfig({
  vite: {
    build: {
      rollupOptions: {
        output: {
          assetFileNames: '_nuxt/my-[name]-[hash].[ext]',
          // or
          assetFileNames: (assetInfo) => {
            if (assetInfo?.name?.endsWith('.css'))
              return '_nuxt/style-[name]-[hash].css'
            return '_nuxt/[name]-[hash].[ext]'
          }
        }
      }
    }
  }
})

This config will affect all assets, including images, fonts, etc.


Merging all *.css files into a single file (cssCodeSplit)

nuxt.config.ts
export default defineNuxtConfig({
  vite: {
    build: {
      cssCodeSplit: false
    }
  }
})

For files stored in the assets/ directory, such as images or fonts, they may be copied to the _nuxt directory. If you prefer to inline all assets instead of copying them, you can configure this using the following setting:

nuxt.config.ts
export default defineNuxtConfig({
  vite: {
    build: {
      assetsInlineLimit: Number.MAX_SAFE_INTEGER
    }
  }
})

πŸ“„ _payload.json

This file is used during SSR or pre-rendering to pass the payload data for the page.

If you don't need this file to be loaded externally and want to prevent it from being generated, you can disable it by setting payloadExtraction to false.

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    payloadExtraction: false
  }
})

πŸ“„ 200.html and 404.html

These files are generated during SSG to serve as fallback pages for SPA websites.

If you don't need them, you can prevent their generation by configuring nitro.prerender.ignore.

nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    prerender: {
      ignore: ['/200.html', '/404.html']
    }
  }
})

πŸ“„ index.html

Nuxt generates an index.html file for every page on your site, serving as the entry point for the static site.

Preload and Prefetch

These Resource Hints help browsers load resources in advance, speeding up the website's loading time.

index.html
<!DOCTYPE html>
<html data-capo="">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="preload" as="fetch" crossorigin="anonymous" href="/_payload.json?93d23bea-82a5-406d-92a4-feb73d5bf1b8">
    <link rel="modulepreload" as="script" crossorigin href="/_nuxt/D9xaLgge.js">
    <link rel="prefetch" as="script" crossorigin href="/_nuxt/BXlAlM0q.js">
    <link rel="prefetch" as="script" crossorigin href="/_nuxt/BRJtm5m-.js">
    <link rel="prefetch" as="script" crossorigin href="/_nuxt/CZvqSstT.js">
    <script type="module" src="/_nuxt/D9xaLgge.js" crossorigin></script>
  </head>
  <body>
    ...
  </body>
</html>

If you need to disable preload and prefetch, you can customize them using Nuxt Hooks. Below is an example showing how to disable these features:

nuxt.config.ts
export default defineNuxtConfig({
  hooks: {
    'build:manifest': (manifest) => {
      console.log('manifest', manifest)
      for (const key in manifest) {
        manifest[key].dynamicImports = []
        manifest[key].prefetch = false
        manifest[key].preload = false
      }
    }
  }
})

Conclusion

Nuxt offers a user-friendly static site generation process, with many optimizations out of the box that save developers from performing repetitive tasks. Whether it’s pre-rendering, asset management, or directory structure, Nuxt handles everything in an organized manner.

At the same time, Nuxt allows for extensive customization, giving developers the flexibility to control every detail of the output. This balance of simplicity and flexibility makes Nuxt an ideal choice for generating efficient static websites.

One More Thing

This article is inspired by my recent open-source project, nuxt-single-html 🌟. It’s a tool that allows you to inline all JavaScript, CSS, and other assets into a single HTML file when generating static sitesβ€”perfect for certain specific use cases. Feel free to check it out!

πŸŽ‰ Β Thanks for reading and hope you enjoy it. Feel free to check out my other posts and find me on X and GitHub!