Injecting Environment Variables into a React App
Tags: react, node, script, git
Published: 2020-02-19 07:53:21

When creating a react app using `create-react-app`, we may sometimes want to inject some environment variables at build time. 
For instance, our team wanted to `console.log` the build time, app version and some `git` information when the app starts.

This is the solution we came up with.

## The Build script

We added a node script to `scripts/build.js`. The script uses `shelljs` to append information to the environment and run `react-scripts` and `simple-git` to execute git commands. 

```javascript
const path = require('path');
const shell = require('shelljs');
const simpleGit = require('simple-git');
const { version } = require('../package.json');

const args = process.argv.slice(2);
const isDev = args.includes('--dev');

if (!shell.which('git')) {
  shell.echo('Cannot build without git');
  shell.exit(1);
}

if (!shell.which('yarn')) {
  shell.echo('Cannot build without yarn');
  shell.exit(1);
}

const workingDir = path.resolve(__dirname, '../');
const git = simpleGit(workingDir);

function getCurrentBranch() {
  return new Promise((fulfill, reject) => {
    git.status((err, status) => {
      if (err) {
        reject(err);
        return;
      }

      fulfill(status.current);
    });
  });
}

function getCurrentCommit() {
  return new Promise((fulfill, reject) => {
    git.revparse(['HEAD'], (err, hash = '') => {
      if (err) {
        reject(err);
        return;
      }
      fulfill(hash.slice(0, 7));
    });
  });
}

(async () => {
  try {
    const branch = await getCurrentBranch();
    const commit = await getCurrentCommit();
    const buildTime = new Date().toUTCString();

    shell.env.REACT_APP_VERSION = version;
    shell.env.REACT_APP_GIT_BRANCH = branch;
    shell.env.REACT_APP_GIT_COMMIT = commit;
    shell.env.REACT_APP_BUILD_TIME = buildTime;

    if (isDev) {
      shell.exec('yarn react-scripts start');
      return;
    }

    shell.exec('yarn react-scripts build');
  } catch (err) {
    shell.echo('Failed to gather build info', err);
    shell.exit(1);
  }
})();

```

We substitute `react-script` in `package.json` for our build script:

```json
...
  "scripts": {
    "start": "node ./scripts/build --dev",
    "build": "node ./scripts/build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "lint": "eslint .",
    "lint:fix": "eslint . --fix"
  }
...

```

---

## In the app

We added `src/utils/printBuilInfo.js`. It prints out our environment variables in a style inspired by `Vue devtools`.

```javascript
function print(title, info) {
  // eslint-disable-next-line
  console.log(
    `%c ${title} %c ${info} %c`,
    'background: #f26d21; padding: 1px; border-radius: 3px 0 0 3px;  color: #fff',
    'background: #009eed; padding: 1px; border-radius: 0 3px 3px 0;  color: #fff',
    'background:transparent',
  );
}

export default function printBuildInfo() {
  print('Version', process.env.REACT_APP_VERSION);
  print('Branch', process.env.REACT_APP_GIT_BRANCH);
  print('Commit', process.env.REACT_APP_GIT_COMMIT);
  print('Build Time', process.env.REACT_APP_BUILD_TIME);
}

```
---

We use it in `src/index.js`:
```javascript
import React from 'react';
import ReactDOM from 'react-dom';
import * as serviceWorker from './serviceWorker';
import App from './App';
import printBuildInfo from './utils/printBuildInfo';
import './styles/index.scss';

printBuildInfo();

ReactDOM.render(<App />, document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
```
---