Skip to main content

Supporting multiple build outputs from one GatsbyJS setup

Published on Wednesday 4, December 2019

As I continue to fiddle with GatsbyJS, one thing I noticed is that you cannot specify the output directory of the GatsbyJS build. This was requested, implemented, and then reverted in GatsbyJS. In fact, it may never become a feature.

The issue did get reopened, but it's a big debate on the feature. I see the build output functionality as a way of scaling a single GatsbyJS application to support multiple end sites. For instance, you can segment content within Drupal in many ways. You can use the Domain module, you can use taxonomy terms, anything you can think of. What if you could use a single GatsbyJS app to conditionally source data based on this info? This way you can build multiple branding sites off of one build from one source. Or you have a Drupal Commerce API server that contains multiple stores and you want different catalog sites.

Luckily, I did come across this issue which has a workaround: https://github.com/gatsbyjs/gatsby/issues/14703. It uses the onPostBuild hook to modify the output directory. So, can this take an environment variable and allow multiple builds? Kind of.

Here's the code for gatsby-node.js, which reads the STOREFRONT environment variable.

// gatsby-node.js
const path = require('path');
const fs = require('fs');

const storefront = process.env.STOREFRONT || 'default';

exports.onPostBuild = () => {
  fs.renameSync(path.join(__dirname, 'public'), path.join(__dirname, `public-${storefront}`));
  fs.mkdirSync(path.join(__dirname, 'public'));
  fs.renameSync(path.join(__dirname, `public-${storefront}`), path.join(__dirname, 'public', storefront));
};

You would then run your builds as:

STOREFRONT=us gatsby build
STOREFRONT=uk gatsby build
STOREFRONT=de gatsby build
STOREFRONT=ca gatsby build

In a perfect world, you would have the following build directories, each with your static site.

public/us
public/uk
public/de
public/ca

Unfortunately, that is not quite the case. Each build ends up purging the last one. That is because the build output is in the public directory. Here's an updated hook that moves the build files into a build directory.

const path = require('path');
const fs = require('fs');

const storefront = process.env.STOREFRONT || 'default';

exports.onPostBuild = () => {
  const outputDir = path.join(__dirname, 'build', `storefront-${storefront}`);
  fs.renameSync(path.join(__dirname, 'public'), outputDir);
};

And now I've got persisting builds!

build/storefront-us
build/storefront-uk
build/storefront-de
build/storefront-ca

It is a bit hacky. It really should purge the directory first and then build.