Appearance
VitePressでブログを作る — 導入から機能追加まで
Markdownで記事を書き、静的サイトとして公開できるブログを作るために、VitePressを使いました。この記事では、導入時のファイル構成から、記事一覧・タグ・検索・関連記事・共有機能を追加した方法まで、実際の実装をもとに紹介します。
VitePressを導入する
既存のGitリポジトリにVitePressを開発依存として追加します。
sh
npm install -D vitepress次に、package.json に開発サーバー、ビルド、プレビューのコマンドを登録します。
json
{
"scripts": {
"dev": "vitepress dev",
"build": "vitepress build",
"preview": "vitepress preview"
}
}このブログでは、設定とテーマを .vitepress/ に、記事を posts/ に置いています。まず必要なファイルを作成し、次のコマンドでローカルでの表示を確認できます。
text
.vitepress/
config.mts
theme/
index.md
posts/
index.mdsh
npm run devサイト共通のタイトルやナビゲーション、検索などは .vitepress/config.mts に設定します。Vueコンポーネントを使ったテーマの拡張は .vitepress/theme/ に分けました。
Markdownの記事を追加する
posts/ にMarkdownファイルを作り、frontmatterにタイトル、概要、日時、タグを記述します。
yaml
---
title: 記事のタイトル
description: 記事の概要
date: 2026-10-16T18:34:00+09:00
tags:
- VitePress
---frontmatterは、Markdownファイルの先頭に置き、--- で囲んで記述するYAML形式のメタデータです。本文とは別に記事の情報を持たせられます。ここでは title と description をページのタイトル・概要に、date を表示と並び順に、tags をタグ一覧や関連記事、タグ検索に使っています。日時にはタイムゾーンを付け、タグは配列として並べます。
本文の先頭にはタイトルと同じH1見出しを置きます。見出しも検索インデックスに含まれるためです。ファイルのパスがURLになり、たとえば posts/first-post.md は /posts/first-post で表示されます。
記事データを共通化して一覧を作る
記事一覧やタグ一覧、関連記事で同じ記事情報を使えるよう、VitePressの createContentLoader でfrontmatterを読み込みます。posts.data.ts では記事一覧ページ自身を除外し、一覧で必要な値に絞ってデータを整形しています。仕組みの要点を抜き出すと、次のようになります。
ts
import { createContentLoader } from 'vitepress'
export default createContentLoader('posts/*.md', {
transform(rawData) {
return rawData
.filter(({ url }) => url !== '/posts/')
.map(({ url, frontmatter }) => ({
title: frontmatter.title,
description: frontmatter.description ?? '',
url,
date: frontmatter.date,
tags: frontmatter.tags ?? [],
}))
},
})実際のローダーでは、frontmatterの値を型チェックし、日時を正規化してから新しい順に並べ替えます。記事一覧の posts/index.md ではこのデータを読み込み、Vueの v-for で記事リンクや日時、タグを描画します。ページ側は表示に集中でき、記事を追加したときに一覧を手で編集する必要はありません。
タグ一覧とタグによる絞り込み
tags/index.md でも同じ記事データを使い、各記事の tags を走査してタグごとにまとめます。タグ名を記事ページからクリックしたときは、/tags/?tag=VitePress のようにクエリ文字列で一覧ページへ渡します。タグ一覧側で URLSearchParams を使って値を読み取り、対象記事を絞り込みます。処理の中心は次のとおりです。
ts
const selectedTag = ref('')
onMounted(() => {
selectedTag.value = new URLSearchParams(window.location.search).get('tag') ?? ''
})
const filteredPosts = computed(() =>
posts.filter((post) => post.tags.includes(selectedTag.value)),
)タグ名をページURLのパスの一部ではなくクエリパラメーターとして扱うことで、タグ別の表示をひとつの一覧ページで実現しています。
日本語検索にタグ検索を加える
VitePressのローカル検索を有効にし、MiniSearchのtokenizerを日本語向けに調整しました。Intl.Segmenter を使って文章を語句に分割し、英字の大小文字もそろえて検索します。
さらに tag:Vite の形式でタグを検索できるようにしました。検索インデックス生成時にfrontmatterのタグから検索専用の語句を作り、本文の語句と区別して登録します。タグ名の部分一致に対応するため、タグの部分文字列も検索用語として作ります。たとえば VitePress というタグは tag:Vite でも見つかりますが、本文にだけ「Vite」がある記事はタグ検索の結果に入りません。
この処理は .vitepress/config.mts のローカル検索設定にまとめています。検索用語はインデックスの生成にだけ使うため、画面に表示される記事本文には追加されません。
関連記事と共有UIをテーマに追加する
記事に付けたタグを使い、同じタグの記事を関連記事として表示します。現在の記事は除外し、タグごとに新しい順で最大3件にしています。.vitepress/theme/RelatedArticles.vue に表示ロジックを置き、画面幅に応じて、広い画面ではサイド領域、それ以外では本文の下に配置します。
テーマの Layout ではVitePressの doc-before、doc-after、aside-top スロットを使い、記事タイトル周辺・記事末尾・サイド領域にコンポーネントを差し込みます。共有ボタンとタグはタイトルの下と本文末尾に表示します。共有ボタンはX、Facebook、LINE、はてなブックマークに対応し、Instagramのアイコンからは、対応ブラウザーでWeb Share APIを使って端末の共有メニューを開きます。記事ごとのOGP・Twitter用メタ情報とcanonical URLは transformHead で設定しています。
ビルドして確認する
変更したら、まず静的サイトをビルドします。続けてプレビューサーバーで生成結果を表示できます。
sh
npm run build
npm run preview一覧や関連記事の表示を確認するための一時記事は posts/_preview/ に置けます。このフォルダーはGitの追跡対象外で、記事データローダーの posts/*.md にも含まれません。テスト記事を使って画面幅やタグ絞り込みを確認したあと、公開用の記事だけを posts/ 直下に置く運用です。
おわりに
VitePressの基本構成を作ったあと、記事データの読み込みを共通化し、そのデータを使って一覧・タグ・関連記事を追加しました。検索や共有UIは設定とテーマコンポーネントに分けています。
記事を追加する作業は、frontmatterと本文を持つMarkdownファイルを作ることが中心です。記事管理の仕組みをサイト側に寄せることで、執筆の手順を増やさずに機能を足していけるブログになりました。