Include Git Commit Hash with Next.js

Inject build information on each builds

ยท 2 min read

When building Web applications, I have the habit of adding which version is currently served. While it does not help users unless a problem is reported, providing the Git commit hash helps me to quickly identify what is currently deployed. I usually add the information in the footer of each pages.

To obtain such information, the following Git command can be used. The --short option provides a short and unique identifier for the current commit.

git rev-parse --short HEAD

When building an application with Next.js, the framework provides a way to define a build identifier which could be used to assign the commit hash. However, the documentation does not specify how to access the value within the application.

In the following example, the Webpack DefinePlugin is used to inject the buildId into the bundle. Any other relevant data that can be computed during build time can be passed along. The plugin will replace every occurrence of an identifier with a given value.

import { execSync } from 'child_process';

const buildTime = Date.now();

function getCommitHash() {
  return execSync('git rev-parse --short HEAD').toString('utf8').trim();
}

/** @type {import('next').NextConfig} */
export default {
  generateBuildId: () => {
    return getCommitHash();
  },
  webpack: (config, { webpack, buildId }) => {
    config.plugins.push(
      new webpack.DefinePlugin({
        BUILD_INFO: JSON.stringify({
          buildId,
          buildTime,
        }),
      }),
    );
    return config;
  },
};

Since the identifier BUILD_INFO will be replaced by a static object, it can then be used in components or any part of your bundled application.

export function Footer() {
  const buildId = BUILD_INFO.buildId;
  const buildTime = new Date(BUILD_INFO.buildTime);
  return (
    <footer>
      <span>{buildId}</span> on{' '}
      <time dateTime={buildTime.toISOString()} title={buildTime.toISOString()}>
        {Intl.DateTimeFormat('en-US', { dateStyle: 'long' }).format(buildTime)}
      </time>
    </footer>
  );
}

When using TypeScript, this global object can also be typed by adding a definition file.

declare interface BuildInfo {
  buildId: string;
  buildTime: number;
}

declare const BUILD_INFO: BuildInfo;

Using this technique always ensures that the data is resolved at build time even in cases where SSR or CSR are used. In some cases, some information have to be computed during this execution phase. This is the case of the commit hash example since the Git repository would most likely not be available once the application deployed.