Using Next.js for Acquia CMS Starter Kits to generate a static site (SSG)

As a static site generator, Next.js comes with two ways to produce your static site. With the default hosting method, Next.js operates as a webserver, intercepting each request giving it two key capabilities: static site revalidation (called Incremental Site Regeneration or ISR) and server side rendering (SSR) for features like image optimization. These capabilities mix compute capabilities with static file serving and requires hosting environments like Acquia NodeJS to operate the Node.js runtime environment.

Image
Nextjs runtime diagram

To use Next.js like this with the Acquia CMS Starter Kits for Drupal, checkout our three part series tutorials:

But there is another way to build sites with Next.js without using Node.js as a web server. This is a more pure JAM Stack method where you use Next.js to build your static site and then use a web server (like Apache or Nginx) to host your static site instead. Though you lose SSR and ISR capabilities with this approach, you gain greater security, resilience and economy (this approach is typically much cheaper to host).

Image
Nextjs SSG Jamstack

 

In this mode, the static files are built by a CI job and hosted on a web server (and/or CDN). The CI job pulls content from Drupal to convert into static HTML files. The images however are still hosted by the Drupal stack as they’re typically statically hosted files anyway.

To use this method of content delivery, you’ll need to slightly modify your Next.js project and change the way you run your CI builds.

Using Next.js export

Firstly, edit your next.config.js file and add the unoptimized: true option to the images configuration:

module.exports = {
  images: {
    domains: [process.env.NEXT_IMAGE_DOMAIN ?? ''],
    unoptimized: true
  },
};

This tells Next.js to not attempt runtime image optimization since Next.js won’t be the webserver. If you don't do this, then Nextjs will complain later when you try to run the export command.

To run an export, your application cannot have any server side dependencies (e.g. getServerSideProps calls). By default, the nextjs starter kit for Acquia CMS Headless Starter Kit only has one server side rendered method which is the next/image component.

Next, in the build CI you’ll want to run the next export command to produce an export directory.

npm install
npx next build
npx next export

This should produce an output directory called out (or pass the -o parameter to use a different name) where your exported HTML site will be hosted. You can change into that directory and run a simple webserver to test out the build:

cd out && php -S localhost:8080

The above command uses PHP to run a web server but is not actually processing any php. Its just a quick and easy way to run a web server locally if you have PHP available.

You’ll be able to visit this site in your browser and see the statically hosted site. Note that when you look at the location of the images that they do not resolve to the same domain as your static site. Instead, they will use the NEXT_IMAGE_DOMAIN value which should be pointing at your Drupal site. This means your static site will depend on both the static hosting of the HTML and the static image hosting of the Drupal site.

You can integrate these commands into CI jobs to push your nextjs export to a static host like Gitlab, Github, Netlify or Cloudflare Pages.

Acquia is currently working on an SSG hosting product where you'll be able to host static sites exported from Nextjs and other Node-based static site generator frameworks.

Limitations of SSG exporting

HTML is generated through a CI build process. If the content in Drupal changes after that CI process has run, it will not be updated in your static site. So the CI process will need to run again. This means you'll need a mechanism for syncing content from the CMS to the static site (e.g. a cron job).

Typically SSG methods like this have to rebuild entire sites which means the CI process will take longer as the content volume grows. For example, if a page takes 0.5s to generate, then a 10 page site takes 5 seconds to generate while a 10,000 page site takes 1.3 hours.

Depending on the business tolerances for how quickly content will update and how much content there is to rebuild will determine how suitable this method of content delivery is for your website. Sites that want realtime content updates or additional backend compute capacity may be better suited to the default method.

Its also worth noting that the images will be raw coming from Drupal as they don't go through Next.js image optimization. This can lead to larger than desired images being delivered to visitors and can be slow, especially on mobile connections. This can be improved by switching Next.js to use an image style from Drupal instead but will have limitations as image styles cannot respond to display resolutions like Next.js image optimization can.