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.```bashnpm init -y
Install TypeScript and
ts-loader: Install the TypeScript compiler and the loader for processing TypeScript files with Webpack.```bashnpm install –save-dev typescript ts-loader webpack webpack-cli
Create
tsconfig.json: Create the TypeScript configuration filetsconfig.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 ] }
Create a TypeScript file: Create a TypeScript file (e.g.,
main.ts) in the./assets/ts/directory.```typescript:assets/ts/main.tsconst 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.```javascript:webpack.config.jsconst 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" . -}}
Update Webpack configuration: Update the Webpack configuration to bundle React components (.tsx files).
```javascript:webpack.config.jsconst 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)