Integrating TypeScript and React into a Hugo Theme

A step-by-step guide to integrating TypeScript and React into a Hugo theme using Webpack, covering tsconfig.json setup, React component mounting, and build configuration.

This article explains how to introduce TypeScript and React when creating a custom Hugo theme. For the basic Hugo setup, please refer to Creating a Free Blog with Markdown (GitHub Pages + Hugo).

1. Initializing the Hugo Theme

You can easily create a theme scaffold using the hugo new theme command.

hugo new theme tomatohugo

Running this command creates a directory named tomatohugo with the basic file and directory structure needed for theme development.

tomatohugo git:(main) tree .
.
├── LICENSE
├── README.md
├── archetypes/
│   └── default.md
├── assets/
│   ├── css/
│   │   └── main.css
│   └── js/
│       └── main.js
├── content/
│   ├── _index.md
│   └── posts/
│       ├── _index.md
│       ├── post-1.md
│       ├── post-2.md
│       └── post-3/
│           ├── bryce-canyon.jpg
│           └── index.md
├── data/
├── hugo.toml
├── i18n/
├── layouts/
│   ├── _default/
│   │   ├── baseof.html
│   │   ├── home.html
│   │   ├── list.html
│   │   └── single.html
│   └── partials/
│       ├── footer.html
│       ├── head/
│       │   ├── css.html
│       │   └── js.html
│       ├── head.html
│       ├── header.html
│       ├── menu.html
│       └── terms.html
├── static/
│   └── favicon.ico
└── theme.toml

15 directories, 25 files
  • LICENSE: Theme license information.
  • README.md: Theme description and usage instructions.
  • archetypes/: Templates for content creation.
  • assets/: Frontend resources such as CSS and JavaScript. Processed by Hugo Pipes.
  • content/: Sample content.
  • data/: Data files used by the theme.
  • hugo.toml: Site-wide configuration file (Hugo 0.112.0+).
  • i18n/: Translation files for multilingual support.
  • layouts/: HTML template files.
  • static/: Static files (images, favicon, etc.). Copied as-is during build.
  • theme.toml: File defining theme settings.

After initialization, you can start a local server with the hugo server command to verify that the simple theme works correctly.

hugo server

Commit example after initialization: https://github.com/yuhi-sa/tomatohugo/commit/695eed912b903388faf16a1791b852697811d348

2. Introducing TypeScript

Set up the Node.js environment and configure Webpack and the TypeScript compiler to use TypeScript within the theme.

  1. Initialize package.json: Run the npm init command at the root of the theme directory to create a package.json file.

    ```bash
    

    npm init -y

  2. Install TypeScript and ts-loader: Install the TypeScript compiler and the loader for processing TypeScript files with Webpack.

    ```bash
    

    npm install –save-dev typescript ts-loader webpack webpack-cli

  3. Create tsconfig.json: Create the TypeScript configuration file tsconfig.json.

    ```json
    

    { “compilerOptions”: { “target”: “es5”, // JavaScript output version “module”: “commonjs”, // Module system “outDir”: “./assets/js/”, // Output directory for compiled JS files “strict”: true // Enable strict type checking }, “include”: [ “./assets/ts/**/*.ts” // TypeScript files to compile ], “exclude”: [ “node_modules” // Directories to exclude ] }

  4. Create a TypeScript file: Create a TypeScript file (e.g., main.ts) in the ./assets/ts/ directory.

    ```typescript:assets/ts/main.ts
    

    const message: string = “Hello, TypeScript!”; console.log(message);

  5. Create webpack.config.js: Create the Webpack configuration file. Webpack is a module bundler for JavaScript applications, used here to bundle TypeScript files.

    ```javascript:webpack.config.js
    

    const path = require(‘path’);

module.exports = { mode: ‘development’, // or ‘production’ entry: ‘./assets/ts/main.ts’, // Entry point module: { rules: [ { test: /\.ts$/, // Target .ts files use: ’ts-loader’, // Use ts-loader exclude: /node_modules/, }, ], }, resolve: { extensions: [’.ts’, ‘.js’], // Extensions to resolve during import }, output: { filename: ‘bundle.js’, // Output file name path: path.resolve(__dirname, ‘assets/js’), // Output directory }, };


6.  **Run the build**:
    Run Webpack to compile TypeScript files into JavaScript and bundle them.

        ```bash

    npx webpack
    ```    This creates`./assets/js/bundle.js`.

7.  **Integration into Hugo templates**:
    To incorporate the generated JavaScript file into Hugo templates, add code like the following in an appropriate location such as `layouts/partials/head/js.html`.

        ```html:layouts/partials/head/js.html

    <!-- layouts/partials/head/js.html -->

    {{- with resources.Get "js/bundle.js" }}
      <script src="{{ .RelPermalink }}"></script>

    {{- end }}
    ```
    PR example for TypeScript introduction: [https://github.com/yuhi-sa/tomatohugo/pull/1](https://github.com/yuhi-sa/tomatohugo/pull/1)

## 3. Introducing React

Add React to the TypeScript environment.

1.  **Install React-related libraries**:
    Install React itself, the DOM manipulation library, and their type definition files.

        ```bash

    npm install --save react react-dom
    npm install --save-dev @types/react @types/react-dom
    ```

2.  **Writing a React component**:
    Since React uses JSX (JavaScript XML) syntax for writing components, change the TypeScript file extension to `assets/ts/main.tsx`.

        ```typescript:assets/ts/main.tsx

    import React from 'react';
    import { createRoot } from 'react-dom/client'; // Recommended API for React 18+

const App = () => {
return (
<div>
<h1>Hello, React!</h1>
</div>
);
};

// Get the mount point element in HTML
const appElement = document.getElementById('app-root'); // Example: id is 'app-root'
if (appElement) {
const root = createRoot(appElement); // Create root with createRoot
root.render(<App />); // Render the component
}
```    **Note**: From React 18 onwards, using`createRoot`instead of`ReactDOM.render`is recommended. Also change the mount point ID in the HTML to`app-root` or similar.

3.  **Integration into Hugo templates**:
    Add an HTML element for calling the React component to the appropriate Hugo template file (e.g., `layouts/_default/single.html` or `layouts/_default/home.html`).

        ```html

    <main>
      <div id="app-root"></div> <!-- React component mount point -->
    </main>

<!-- Load JavaScript -->

{{- partial "head/js.html" . -}}
  1. Update Webpack configuration: Update the Webpack configuration to bundle React components (.tsx files).

    ```javascript:webpack.config.js
    

    const path = require(‘path’);

module.exports = { mode: ‘development’, entry: ‘./assets/ts/main.tsx’, // Change entry point to .tsx module: { rules: [ { test: /\.tsx?$/, // Target .ts or .tsx files use: ’ts-loader’, exclude: /node_modules/, }, ], }, resolve: { extensions: [’.ts’, ‘.tsx’, ‘.js’], // Add .tsx to extensions }, output: { filename: ‘bundle.js’, path: path.resolve(__dirname, ‘assets/js’), }, };


5.  **Verification**:
    Verify the setup by starting the local server and confirming that "Hello, React!" is displayed.

        ```bash

    hugo server
    ```
    PR example for React introduction: [https://github.com/yuhi-sa/tomatohugo/pull/2](https://github.com/yuhi-sa/tomatohugo/pull/2)