While migrating my site from Orchard to Hugo I wanted to add some JavaScripts. Specifically I wanted that to power the search experience using some simple JavaScripts. However, I’ve grown quite fond over using React (and TSX/JSX) to any kind of user experiences for the web and I did not want to go back to pure JavaScript or use some DOM manipulation scripts such as jQuery.

Hugo, that I use for my static site, does not directly have support for transpiling React. But with a few small steps you can make React transpiling as a part of your Hugo build and use React for your user experiences. Yes, this might not be new to everyone, but I did not find a direct guide on how to set this up - specifically for Hugo noobs such as me. So here’s a quick guide on how to get started with Hugo and React.

Preparing your site

First of all you will need to prepare your site with the necessary transpiling tools, and we will use Babel.js for this, as documented in the Hugo documentation. Babel.js also gives you lots of extra features that allows you to transpile into code that works cross-browser and more. The first step is of course to install Babel.js into your Hugo site. And for all of you in the node.js world this will feel very familiar! We need to initialize node.js in your Hugo site using and then install Babel.js using the following commands:

npm init
npm install @babel/cli @babel/core @babel/preset-react --save

Next step is to setup our Babel.js configuration which we store in a JavaScript file in the root of the Hugo site. It should have the content below. This is also the file where you might want to add the necessary configurations for supporting older browsers using the @babel/preset-env preset.

module.exports = function (api) {
  api.cache(true);
  const presets = [
    ["@babel/preset-react"]
    ]
  const plugins = [];
  return {
    presets,
    plugins
  };
}

Creating the React script

Next one is all up to you. Create the React script in the way you prefer. For this demo we’ll use a simple a simple React hooks demo. The file is added to the /assets/script folder and is called app.jsx.

function Example() {
    const [count, setCount] = React.useState(0);
  
    return (
      <div>
        <p>You clicked {count} times</p>
        <button onClick={() => setCount(count + 1)}>
          Click me
        </button>
      </div>
    );
  }
ReactDOM.render(React.createElement(Example),
    document.getElementById("app"));

Adding the script to your pages

Where you add the scripts is up to you, choose the Hugo template of your choice. What you need to do is to first add the React scripts, I’m using the CDN for it in this demo. Then you need to read your script from the resources, pipe it through Babel, into a JavaScript file which you reference in your template.

<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
{{ $app := resources.Get "scripts/app.jsx" | resources.ExecuteAsTemplate "app.js" . | babel  }}
<script src="{{ $app.RelPermalink }}"></script>

React working

That is all! Now when building your Hugo site your JSX file will be passed through Babel, using the React presets specified in the Babel.js configuration, and create a JavaScript file.

Note: If you are using Github Workflows to build your site - or any other similar CI/CD solution - you must remember to run the command npm install before you run your Hugo build command.