Cal-Heatmapを使ってブログのアクティビティを表示していたけど(当時のメモ)、本家GitHugのActivity Calendarを再現しようとすると、微妙に痒いところに手が届かない1。もっと本家GitHubに寄せたライブラリないかな、と物色していたところ、react-activity-calendarを見つけた。最近もメンテナンスされてそうなので、これに切り替えることにした。
react-activity-calendarはその名の通りReact用のコンポーネントなので、このままではHugoからは使えない。
スタンドアロンのJSにビルドしようかとCopilotに相談したら、Hugoのjs.Buildという機能を教えてもらった。サイトビルド時にesbuildを呼び出して、tsファイルやjsxファイルをトランスパイルしてくれるらしい。Hugo、なんでもあるな…
というわけで、jsxで書いたコンポーネントをあっさりHugoから呼び出せたので、そのメモ。
1. ライブラリのインストール
js.Build
の依存ライブラリの解決はpackage.json
を参照する。というわけで、まずは必要なライブラリのインストール
npm install react react-dom react-activity-calendar
2. JSXの作成
Reactで開発するのと同様にjsxファイルを書く。例えばBlogActivity.jsx
というファイル名にして、assets/js
に配置する。
import React from 'react';
import ReactDOM from 'react-dom/client';
import { ActivityCalendar } from 'react-activity-calendar';
const data = [
{ date: '2025-02-26', count: 2, level: 1 },
{ date: '2025-04-10', count: 16, level: 4 },
{ date: '2025-06-15', count: 11, level: 3 },
{ date: '2025-08-26', count: 0, level: 0 },
];
function BlogActivity() {
return <ActivityCalendar data={data} />;
}
const root = ReactDOM.createRoot(document.getElementById('cal-heatmap'));
root.render(<BlogActivity />);
3. Hugoのテンプレートに埋め込む
js.Build
を使ってビルド。ついでに、Minifyもしておく。
<div id="cal-heatmap"></div>
{{- with resources.Get "js/BlogActivity.jsx" -}}
{{- $opts := dict
"targetPath" "js/blog-activity.min.js"
"minify" true -}}
{{- $js := . | js.Build $opts -}}
<script src="{{ $js.RelPermalink }}"></script>
{{- end -}}
上記jsxだと以下のようにレンダリングされる。下記は jsbuild
という、上記埋め込みと同等のshortcodesを用意してレンダリングしている。
あとは、サイズや色、label変えたり、イベントハンドラを仕込めば完成。以前より、より本家GitHubの見た目に近づいたと思う。

8/26時点でのキャプチャ
Page Insights
ちなみに、Reactのライブラリをまるっと抱えることになるため、パフォーマンスはやや悪化する。これまで約150KBで済んでいたファイルが400KB弱に膨れている。下記はモバイル時の計測結果2。

before

after
ただ、Cal-Heatmapに比べると、D3フリーでSVGでレンダリングするreact-activity-calendarの方が、体感として描画は早くなっている気がする。
気が向いたら、この辺のモバイル向け最適化もやろうと思う。
例えば、
subDomain
でghDay
を指定した時、開始日と終了日が月単位で丸められるという仕様 ↩︎デスクトップはほぼ誤差でそもそもパフォーマンスは悪くない ↩︎