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.

npm init -y ```

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

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

  1. Create tsconfig.json: Create the TypeScript configuration file tsconfig.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 ] } ```

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

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

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

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 }, }; ```

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

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

  1. 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.

{{- with resources.Get “js/bundle.js” }}

{{- end }} ``` PR example for TypeScript introduction: 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.

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

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

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

const App = () => { return (

Hello, React!

); };

// 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(); // 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.

  1. 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).

{{- partial “head/js.html” . -}} ```

  1. Update Webpack configuration: Update the Webpack configuration to bundle React components (.tsx files).

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’), }, }; ```

  1. Verification: Verify the setup by starting the local server and confirming that “Hello, React!” is displayed.

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