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.
Initialize
package.json: Run thenpm initcommand at the root of the theme directory to create apackage.jsonfile.
npm init -y ```
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 ```
Create
tsconfig.json: Create the TypeScript configuration filetsconfig.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 ] } ```
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); ```
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 }, }; ```
Run the build: Run Webpack to compile TypeScript files into JavaScript and bundle them.
npx webpack
```
This creates ./assets/js/bundle.js.
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.
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 ```
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(createRoot instead of ReactDOM.render is recommended. Also change the mount point ID in the HTML to app-root or similar.
Integration into Hugo templates: Add an HTML element for calling the React component to the appropriate Hugo template file (e.g.,
layouts/_default/single.htmlorlayouts/_default/home.html).
{{- partial “head/js.html” . -}} ```
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’), }, }; ```
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