長文記事と短い投稿を1つのHugoサイトに同居させた

シリーズ「Hugo移行記」の記事

作業中に気づいたことを、その場で短く書き残したいときがあります。1つの判断や、小さな修正だけなら、長文記事の構成には合いません。

別の投稿サービスへ分けるのではなく、自分のブログに「ショート」という短い投稿欄を作りました。同じサイトなら、デザインとタグを記事と共有しながら、見せ方だけを分けられます。

この記事では、長文記事とショートを同じHugoサイトへ載せるため、表示方法をどう分けたかを書きます。対象は、投稿種別、一覧内の共有URL、追加投稿の表示、共通タグの4点です。確認したのはHugo v0.164.0(2026年7月時点)です。

「Hugo移行記」のうち、記事とショートの公開画面を扱う記事です。先行記事を読まなくても、この記事だけで分かるように書いています。

まず全体像

1つのHugoサイトでデザインとタグを共有し、長文記事は個別ページ、ショートは一覧カード、共有アンカー、追加投稿に分ける構成図
保存場所と表示種別を分け、デザインとタグは同じサイトのものを使います。

保存場所と表示種別を分けた

Hugoのセクションは、基本的に content/ 直下のフォルダで分かれます。このブログでは、記事を content/posts/<年>/、ショートを content/shorts/<年>/ に置きました。

ショートのfront matter(投稿冒頭の設定欄)には、表示種別を示す type: "shorts" を書いています。

---
title: "ショートを、一覧の流れを残したまま共有できるURLにした"
date: 2026-07-29T09:00:00+09:00
slug: "short-list-anchor-urls"
type: "shorts"
tag: ["Hugo", "UI設計"]
icon: "🔗"
---

type は、どのテンプレートで表示するかを選ぶための値です。公開後のショートは保存場所だけでも判別できますが、下書きでは事情が変わります。

ここでのテンプレートは、投稿データからページのHTMLを作るファイルです。front matterの slug は、URLに使う識別名を表します。

このブログは、未公開の投稿を content/drafts/ に置きます。下書き中のショートは content/drafts/shorts/ にあるため、保存場所だけを見ると記事の下書きと同じ drafts セクションです。

そこで、一覧を作るテンプレートでは Type も見ています。

{{ $articlePages := where
  (where site.RegularPages "Section" "in" (slice "posts" "drafts"))
  "Type" "ne" "shorts"
}}

記事欄には、posts または drafts にあり、表示種別が shorts ではない投稿を出します。ショート欄には、保存場所にかかわらず Typeshorts の投稿だけを出します。

保存場所は公開状態、type は表示方法を担当します。この分け方なら、下書き確認でも公開後と同じショート用のカードを使えます。

共有URLはショート一覧の中を指す

Hugoは、ショートにも日付とslugを使った個別ページを生成します。

/shorts/<年>/<月>/<日>/<slug>/

ただし、カードの選択とコピーボタンで共有するのは個別ページではありません。ショート一覧の中にあるカードの位置を、URL末尾のアンカーで指します。

/shorts/#short-<固有ID>

カードのHTMLでは、同じ固有IDを id と共有URLの両方へ使っています。

<article
  id="short-{{ .File.UniqueID }}"
  data-short-url="{{ "shorts/" | relURL }}#short-{{ .File.UniqueID }}">

.File.UniqueID は、Hugoが投稿ファイルから作る固有IDです。HTMLの id は、一覧内でカードの位置を特定するために使います。

カードを選ぶと、ブラウザで動くJavaScriptが画面を移動せずにアドレスバーのURLを更新します。直接そのURLを開いたときは、ブラウザが該当カードまで移動し、JavaScriptが選択枠を付けます。コピーボタンも同じ一覧内アンカーを返します。

個別ページをなくしたわけではありません。Hugoは個別ページを生成したまま、カード選択とコピーボタンから使う共有URLだけを一覧へ向けています。共有された1件を読んだあと、そのまま前後のショートへ移れることを優先しました。

追加投稿はHTMLコメントで区切る

同じ話題に進展や訂正があったときは、元の文章を消さず、同じショートへ追加投稿を足します。

区切りには <!--thread--> というHTMLコメントを使っています。

最初の投稿本文。

<!--thread-->

追加投稿の本文。

ショート一覧のテンプレートは、この文字列でMarkdownを分割します。最初の投稿は常に表示し、それ以外の投稿は「スレッドを開く」の中へ並べます。個別ページでは、すべての投稿を最初から表示します。

Hugoのショートコードを使わなかったのは、本文へHugo専用の呼び出しを入れないためです。別のツールへ移る場合は同じ分割処理が必要ですが、区切り文字と本文はMarkdownファイルに残ります。

タグは記事とショートで共有する

長文記事とショートは、同じ tag を使います。たとえば Hugo タグのページには、Hugoについて書いた記事とショートの両方が集まります。

同じ一覧へ混ぜて並べるのではなく、上部の「記事」「ショート」で表示を切り替えます。

/tag/hugo/?type=posts
/tag/hugo/?type=shorts

テンプレートは各項目へ、記事かショートかを表す posts または shorts の値を付けます。JavaScriptはURL末尾の type を読み、選ばれた側だけを表示します。

記事にはカテゴリとシリーズもありますが、ショートはタグだけです。短い投稿へ分類を増やさず、同じ話題を追う入口だけを記事と共有しています。

実装で確認したこと

下書きと未来日付を含めてビルドし、次の4点を確認しました。

  1. content/drafts/shorts/ の投稿が、記事欄ではなくショート欄へ出る
  2. ショートカードの id、選択時のURL、コピーボタンのURLが同じアンカーを使う
  3. <!--thread--> より後の本文が、一覧では追加投稿として開閉できる形になる
  4. タグ一覧の各項目に記事またはショートの種別が付き、切り替えリンクが生成される

保存場所、表示方法、共有URL、追加投稿の区切りを別々にしたため、共有URLを変えても投稿ファイルの置き場所は変えずに済みます。

まとめ

長文記事とショートを同居させるため、保存場所だけに判定を任せず、type: "shorts" で表示方法を分けました。共有URLは一覧内アンカーへ向け、追加投稿は <!--thread--> で区切り、タグだけを記事と共有しています。

短い投稿を別のサービスへ分けず、自分のサイトでは長文記事を個別ページ、ショートを一覧カードで読める形にしました。