Skip to content

VitePressでブログを作る — 導入から機能追加まで

タグ

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.md
sh
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ファイルを作ることが中心です。記事管理の仕組みをサイト側に寄せることで、執筆の手順を増やさずに機能を足していけるブログになりました。

Markdownで書く個人ブログ