Hugoブログを運営していると、読者に他の記事も読んでもらうために「関連記事」を表示したくなります。 CMSなどでは簡単に表示できると思うのですが、Hugoではちょっと調べる必要があり面倒でした。 今回、Hugoでの関連ページを実装したのでメモを残しておきます。
Hugoには標準で Related Content という機能がデフォルトで備わっており、複雑なコードを書かずに実装可能です。以下のコードで5記事関連ページ表示できます。ただし、文字とリンクだけで味気ないです。
layouts/partials/related.html
{{ $related := .Site.RegularPages.Related . | first 5 }}
{{ with $related }}
<h3>See Also</h3>
<ul>
{{ range . }}
<li><a href="{{ .RelPermalink }}">{{ .Title }}</a></li>
{{ end }}
</ul>
{{ end }}本ブログで実装しているHugoでの関連記事の実装内容を以下に記載していきます。
1. Hugoの設定ファイル (hugo.toml) の編集 #
何をもって「関連」とするかのルールを hugo.toml(または config.toml)に定義します。一般的には「同じタグ」や「同じキーワード」を持つ記事を優先させるようです。
デフォルト状態ですでに以下のように記載されていました。
[related]
threshold = 0
toLower = false
[[related.indices]]
name = "tags"
weight = 100
[[related.indices]]
name = "categories"
weight = 100
[[related.indices]]
name = "series"
weight = 50
[[related.indices]]
name = "authors"
weight = 20
[[related.indices]]
name = "date"
weight = 10
[[related.indices]]
applyFilter = false
name = 'fragmentrefs'
type = 'fragments'
weight = 102. 関連記事を表示するテンプレートの作成 #
次に、記事の下部に表示するためのパーシャルテンプレートを作成します。
例:layouts/partials/related.html
{{/* .Params.showRelated が false に設定されていない場合のみ実行 */}}
{{ if ne .Params.showRelated false }}
{{/* where関数を使って 'Section' が 'posts' のものだけを抽出 */}}
{{ $related := .Site.RegularPages.Related . }}
{{ $related = where $related "Section" "posts" | first 3 }}
{{ with $related }}
<h3 class="mt-8 text-xl font-bold">関連ページ</h3>
<div class="related-cards-container">
{{ range . }}
<div class="link-card-wrapper" style="margin: 15px 0; max-width: 600px;">
<a href="{{ .RelPermalink }}" class="link-card"
style="text-decoration: none; color: inherit; display: flex; border: 1px solid #ddd; border-radius: 8px; overflow: hidden; background: #fff; height: 110px;">
{{/* 画像の取得ロジック */}}
{{ $image_url := "" }}
{{ $featured := .Resources.GetMatch "featured.webp" }}
{{ if $featured }}
{{/* 記事フォルダ内に featured.webp がある場合 */}}
{{ $image_url = $featured.RelPermalink }}
{{ else }}
{{/* ない場合は Front Matter のパラメータを確認 */}}
{{ $img_param := .Params.featureImage | default .Params.image }}
{{ if $img_param }}
{{ $image_url = $img_param | relURL }}
{{ end }}
{{ end }}
{{/* 画像の表示部分 */}}
{{ if $image_url }}
<div class="link-card-image"
style="flex: 0 0 140px; background-image: url('{{ $image_url }}'); background-size: cover; background-position: center;">
</div>
{{ else }}
<div class="link-card-image"
style="flex: 0 0 140px; background: #f0f0f0; display: flex; align-items: center; justify-content: center; color: #ccc;">
No Image</div>
{{ end }}
<div class="link-card-content"
style="flex: 1; padding: 12px; display: flex; flex-direction: column; justify-content: space-between; overflow: hidden;">
<div class="link-card-title"
style="font-weight: bold; font-size: 0.95rem; color: #333; display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;">
{{ .Title }}
</div>
<div class="link-card-meta"
style="display: flex; align-items: center; gap: 8px; font-size: 0.75rem; color: #888;">
<img src="/favicon.png" alt="" style="width: 14px; height: 14px; object-fit: contain;">
<span>{{ .Date.Format "2006.01.02" }}</span>
</div>
</div>
</a>
</div>
{{ end }}
</div>
{{ end }}
{{ end }}- サムネイルはwebp形式にしています。
- 関連ページを表示させたくないページでは、以下のフラグで非表示にしています。
showRelated: false