Preact でミニブログを 1 日で公開:ルーティング・MDX・SEO まで一気通貫

軽量で高速な Preact を使えば、本格的なブログを驚くほど短時間で構築できます。React と互換性がありながらわずか 3KB という軽さで、SEO に配慮したブログサイトを 1 日で完成させることも可能でしょう。
この記事では、Preact によるミニブログ構築の全工程を解説します。ルーティング設定から MDX による記事管理、SEO 対策まで、実務で使える技術を一気通貫でお伝えしますね。
背景
Preact の特徴と選択理由
Preact は React の軽量代替ライブラリとして知られ、以下の特徴を持っています。
# | 項目 | 説明 |
---|---|---|
1 | サイズ | わずか 3KB(gzip 圧縮時)で React の約 10 分の 1 |
2 | 互換性 | React とほぼ同じ API を提供し、既存知識を活用可能 |
3 | パフォーマンス | Virtual DOM の最適化により高速レンダリング |
4 | 学習コスト | React 経験者なら即座に移行できる |
ブログのような静的コンテンツ中心のサイトでは、JavaScript のバンドルサイズが読み込み速度に直結します。Preact を選択することで、初回表示速度を大幅に改善でき、SEO スコアの向上も期待できるでしょう。
モダンブログに求められる要件
現代の技術ブログには、以下の要件が必須となっています。
mermaidflowchart TB
blog["ブログサイト"] --> routing["ルーティング<br/>機能"]
blog --> content["コンテンツ<br/>管理"]
blog --> seo["SEO 対策"]
routing --> dynamic["動的ページ遷移"]
routing --> spa["SPA 体験"]
content --> mdx["MDX 記述"]
content --> syntax["シンタックス<br/>ハイライト"]
seo --> meta["メタタグ最適化"]
seo --> sitemap["サイトマップ生成"]
seo --> ogp["OGP 設定"]
この図は、ブログ構築に必要な 3 つの柱と、それぞれの実装要素を示しています。
これらの要件を満たすため、Preact エコシステムの各種ツールを組み合わせて構築していきます。
課題
従来のブログ構築における問題点
静的サイトジェネレーターや WordPress を使った従来のブログ構築には、いくつかの課題があります。
# | 課題 | 影響 |
---|---|---|
1 | セットアップの複雑さ | 環境構築に時間がかかり、初日に公開できない |
2 | バンドルサイズの肥大化 | React ベースのフレームワークは初期ロードが遅い |
3 | カスタマイズの制約 | テーマやプラグインの制限で自由度が低い |
4 | ビルド時間の長さ | 記事数が増えると再ビルドに時間がかかる |
特に個人ブログや技術ブログでは、シンプルで軽量な構成が望ましいものです。
Preact 特有の課題
Preact を選択する際には、以下の点に注意が必要でしょう。
mermaidflowchart LR
challenge["Preact 利用時の<br/>課題"] --> ecosystem["エコシステムの<br/>少なさ"]
challenge --> ssr["SSR/SSG の<br/>設定複雑さ"]
challenge --> plugin["プラグインの<br/>互換性"]
ecosystem --> solution1["preact-compat で<br/>React ライブラリ利用"]
ssr --> solution2["Preact CLI または<br/>Vite を活用"]
plugin --> solution3["Preact 専用<br/>パッケージ選定"]
この図は、Preact 固有の課題とその解決アプローチを示しています。
これらの課題を理解した上で、適切なツールと設計パターンを選択することが重要です。
解決策
プロジェクト構成の全体像
Preact でブログを構築する際の推奨構成を示します。
mermaidflowchart TB
subgraph build["ビルドツール"]
vite["Vite<br/>(高速バンドル)"]
end
subgraph framework["フレームワーク"]
preact["Preact<br/>(コア)"]
preactRouter["preact-router<br/>(ルーティング)"]
end
subgraph content_management["コンテンツ管理"]
mdx["MDX<br/>(記事記述)"]
frontmatter["gray-matter<br/>(メタデータ解析)"]
end
subgraph styling["スタイリング"]
css["CSS Modules"]
end
subgraph seo_tools["SEO ツール"]
helmet["preact-helmet<br/>(メタタグ)"]
sitemap_gen["sitemap.js<br/>(サイトマップ)"]
end
vite --> preact
preact --> preactRouter
preact --> mdx
mdx --> frontmatter
preact --> helmet
preact --> css
この構成により、最小限の依存関係で必要な機能をすべて実装できます。
技術スタック選定の理由
各技術を選定した理由を整理します。
# | 技術 | 理由 |
---|---|---|
1 | Vite | Preact との相性が良く、開発サーバーが高速 |
2 | preact-router | シンプルな API でルーティングを実装可能 |
3 | MDX | React/Preact コンポーネントを Markdown 内で利用可能 |
4 | gray-matter | Frontmatter の解析が簡単 |
5 | preact-helmet | メタタグの動的管理に対応 |
この組み合わせにより、開発体験と実行パフォーマンスの両立が実現できるでしょう。
具体例
プロジェクトのセットアップ
まず、Vite を使って Preact プロジェクトを初期化します。
bashyarn create vite my-preact-blog --template preact
cd my-preact-blog
yarn install
このコマンドで、Preact + Vite の基本構成が作成されます。開発サーバーは yarn dev
で起動可能です。
次に、必要なパッケージをインストールしましょう。
bashyarn add preact-router preact-helmet gray-matter
yarn add -D @mdx-js/rollup remark-gfm rehype-highlight
これらのパッケージで、ルーティング、SEO、MDX サポートを実現します。
Vite の設定
vite.config.js
を編集して MDX サポートを追加します。
javascriptimport { defineConfig } from 'vite';
import preact from '@preact/preset-vite';
import mdx from '@mdx-js/rollup';
import remarkGfm from 'remark-gfm';
import rehypeHighlight from 'rehype-highlight';
export default defineConfig({
plugins: [
// MDX プラグインを Preact より先に配置
mdx({
remarkPlugins: [remarkGfm], // GitHub Flavored Markdown サポート
rehypePlugins: [rehypeHighlight], // シンタックスハイライト
}),
preact(),
],
});
この設定により、.mdx
ファイルを Preact コンポーネントとしてインポートできるようになります。
MDX のプラグインオプションでは、GFM(テーブルやタスクリストなど)とコードハイライトを有効化しています。
プロジェクトディレクトリ構造
ブログアプリケーションのディレクトリ構造を設計します。
csharpmy-preact-blog/
├── src/
│ ├── components/ # 再利用可能なコンポーネント
│ │ ├── Header.jsx
│ │ ├── Footer.jsx
│ │ └── ArticleCard.jsx
│ ├── pages/ # ページコンポーネント
│ │ ├── Home.jsx
│ │ ├── Article.jsx
│ │ └── NotFound.jsx
│ ├── posts/ # MDX 記事ファイル
│ │ ├── first-post.mdx
│ │ └── second-post.mdx
│ ├── utils/ # ユーティリティ関数
│ │ └── posts.js
│ ├── app.jsx # ルートコンポーネント
│ └── main.jsx # エントリーポイント
├── public/ # 静的ファイル
└── vite.config.js
この構造により、コンポーネントと記事を明確に分離し、保守性を高めています。
ルーティングの実装
src/app.jsx
でアプリケーション全体のルーティングを設定しましょう。
javascriptimport { h } from 'preact';
import Router from 'preact-router';
import Header from './components/Header';
import Footer from './components/Footer';
import Home from './pages/Home';
import Article from './pages/Article';
import NotFound from './pages/NotFound';
まず必要なモジュールをインポートします。preact-router
がルーティングの中核です。
次に、アプリケーションコンポーネントを定義します。
javascriptexport function App() {
return (
<div id='app'>
<Header />
<main>
<Router>
<Home path='/' />
<Article path='/posts/:slug' />
<NotFound default />
</Router>
</main>
<Footer />
</div>
);
}
Router
コンポーネント内で各ページを定義し、path
プロパティで URL パターンを指定します。:slug
は動的パラメータです。
ヘッダーコンポーネントの実装
src/components/Header.jsx
でサイトヘッダーを作成します。
javascriptimport { h } from 'preact';
import { Link } from 'preact-router/match';
import './Header.css';
export default function Header() {
return (
<header className='header'>
<nav className='nav'>
<Link href='/' className='logo'>
My Tech Blog
</Link>
<ul className='nav-links'>
<li>
<Link href='/'>ホーム</Link>
</li>
<li>
<Link href='/about'>About</Link>
</li>
</ul>
</nav>
</header>
);
}
preact-router
の Link
コンポーネントを使うことで、SPA 風のページ遷移が実現できます。
記事一覧ページの実装
src/pages/Home.jsx
でトップページを作成しましょう。
javascriptimport { h } from 'preact';
import { useState, useEffect } from 'preact/hooks';
import { Helmet } from 'preact-helmet';
import { getAllPosts } from '../utils/posts';
import ArticleCard from '../components/ArticleCard';
import './Home.css';
必要なフックと記事取得関数をインポートします。preact-helmet
で SEO 対策を行います。
次に、コンポーネント本体を実装します。
javascriptexport default function Home() {
const [posts, setPosts] = useState([]);
useEffect(() => {
// 記事一覧を非同期で取得
getAllPosts().then(setPosts);
}, []);
return (
<div className='home'>
<Helmet>
<title>ホーム | My Tech Blog</title>
<meta
name='description'
content='Preact で構築した技術ブログです'
/>
<meta property='og:title' content='My Tech Blog' />
<meta property='og:type' content='website' />
</Helmet>
<h1>最新記事</h1>
<div className='articles-grid'>
{posts.map((post) => (
<ArticleCard key={post.slug} post={post} />
))}
</div>
</div>
);
}
useEffect
で初回レンダリング時に記事を取得し、Helmet
でページのメタ情報を設定しています。
記事カードコンポーネント
src/components/ArticleCard.jsx
で記事一覧のカード表示を実装します。
javascriptimport { h } from 'preact';
import { Link } from 'preact-router/match';
import './ArticleCard.css';
export default function ArticleCard({ post }) {
const { slug, title, description, date, tags } = post;
return (
<article className='article-card'>
<Link href={`/posts/${slug}`}>
<h2>{title}</h2>
<p className='description'>{description}</p>
<div className='meta'>
<time>
{new Date(date).toLocaleDateString('ja-JP')}
</time>
<div className='tags'>
{tags?.map((tag) => (
<span key={tag} className='tag'>
{tag}
</span>
))}
</div>
</div>
</Link>
</article>
);
}
記事のメタデータを受け取り、カード形式で表示します。クリックすると記事詳細ページに遷移する仕組みです。
記事データ管理ユーティリティ
src/utils/posts.js
で記事データの取得ロジックを実装しましょう。
javascript// すべての MDX ファイルを動的インポート
const postModules = import.meta.glob('../posts/*.mdx', {
eager: true,
});
/**
* すべての記事データを取得
* @returns {Promise<Array>} 記事オブジェクトの配列
*/
export async function getAllPosts() {
const posts = [];
for (const path in postModules) {
const mod = postModules[path];
const slug = path
.replace('../posts/', '')
.replace('.mdx', '');
posts.push({
slug,
...mod.frontmatter, // frontmatter からメタデータを取得
});
}
// 日付の降順でソート
return posts.sort(
(a, b) => new Date(b.date) - new Date(a.date)
);
}
Vite の import.meta.glob
を使って、すべての MDX ファイルを自動的に読み込んでいます。
次に、特定の記事を取得する関数を追加します。
javascript/**
* スラッグから記事データを取得
* @param {string} slug - 記事のスラッグ
* @returns {Promise<Object|null>} 記事オブジェクト
*/
export async function getPostBySlug(slug) {
const mod = postModules[`../posts/${slug}.mdx`];
if (!mod) return null;
return {
slug,
...mod.frontmatter,
Component: mod.default, // MDX コンポーネント本体
};
}
この関数は、記事の内容を表示するために必要な MDX コンポーネントを返します。
MDX 記事ファイルの作成
src/posts/first-post.mdx
で実際の記事を作成しましょう。
mdx---
title: 'Preact で始める軽量 Web アプリ開発'
description: 'Preact の基本から実践まで、軽量フレームワークの魅力を解説します'
date: '2025-01-15'
tags: ['Preact', 'JavaScript', 'フロントエンド']
---
# はじめに
Preact は React の軽量代替として注目されているライブラリです。
わずか 3KB という小ささながら、React とほぼ同じ API を提供しています。
# Preact の特徴
Preact が選ばれる理由は以下の通りです。
- **軽量**: gzip 圧縮時わずか 3KB
- **高速**: 仮想 DOM の最適化により高速レンダリング
- **互換性**: React エコシステムとの互換性
# まとめ
Preact を使えば、パフォーマンスを犠牲にせず、
モダンな Web アプリケーションを構築できるでしょう。
Frontmatter(---
で囲まれた部分)にメタデータを記述し、本文は通常の Markdown で記述します。
記事詳細ページの実装
src/pages/Article.jsx
で個別記事を表示するページを作成します。
javascriptimport { h } from 'preact';
import { useState, useEffect } from 'preact/hooks';
import { Helmet } from 'preact-helmet';
import { getPostBySlug } from '../utils/posts';
import './Article.css';
記事取得関数と必要なモジュールをインポートします。
コンポーネント本体を実装しましょう。
javascriptexport default function Article({ slug }) {
const [post, setPost] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
// スラッグから記事を取得
getPostBySlug(slug).then((data) => {
setPost(data);
setLoading(false);
});
}, [slug]);
if (loading) {
return <div className='loading'>読み込み中...</div>;
}
if (!post) {
return (
<div className='not-found'>記事が見つかりません</div>
);
}
const { Component, title, description, date, tags } =
post;
return (
<article className='article'>
<Helmet>
<title>{title} | My Tech Blog</title>
<meta name='description' content={description} />
<meta property='og:title' content={title} />
<meta
property='og:description'
content={description}
/>
<meta property='og:type' content='article' />
</Helmet>
<header className='article-header'>
<h1>{title}</h1>
<div className='article-meta'>
<time>
{new Date(date).toLocaleDateString('ja-JP')}
</time>
<div className='tags'>
{tags?.map((tag) => (
<span key={tag} className='tag'>
{tag}
</span>
))}
</div>
</div>
</header>
<div className='article-content'>
<Component />
</div>
</article>
);
}
MDX から取得した Component
をレンダリングすることで、記事本文が表示されます。SEO 対策として、各記事固有のメタタグも設定していますね。
シンタックスハイライトのスタイル追加
コードブロックを見やすくするため、highlight.js のスタイルを追加します。
src/main.jsx
でスタイルシートをインポートしましょう。
javascriptimport { render } from 'preact';
import { App } from './app';
import './index.css';
// シンタックスハイライト用スタイル
import 'highlight.js/styles/github-dark.css';
render(<App />, document.getElementById('app'));
これで、記事内のコードブロックが適切にハイライト表示されるようになります。
SEO 対策の実装
サイト全体の SEO 設定を src/app.jsx
に追加します。
javascriptimport { h } from 'preact';
import { Helmet } from 'preact-helmet';
import Router from 'preact-router';
// ... その他のインポート
export function App() {
return (
<div id='app'>
<Helmet
defaultTitle='My Tech Blog'
titleTemplate='%s | My Tech Blog'
>
<html lang='ja' />
<meta charset='utf-8' />
<meta
name='viewport'
content='width=device-width, initial-scale=1'
/>
<meta name='theme-color' content='#673ab8' />
</Helmet>
<Header />
<main>
<Router>
<Home path='/' />
<Article path='/posts/:slug' />
<NotFound default />
</Router>
</main>
<Footer />
</div>
);
}
Helmet
の defaultTitle
と titleTemplate
により、各ページで統一感のあるタイトル表示が可能です。
サイトマップ生成スクリプト
scripts/generate-sitemap.js
でサイトマップを自動生成します。
javascriptimport fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(
fileURLToPath(import.meta.url)
);
const postsDir = path.join(__dirname, '../src/posts');
const siteUrl = 'https://your-blog.com';
まず、必要なモジュールと定数を定義します。siteUrl
は実際のドメインに置き換えてください。
サイトマップの XML を生成する処理を実装しましょう。
javascript// MDX ファイルからスラッグを取得
const posts = fs
.readdirSync(postsDir)
.filter((file) => file.endsWith('.mdx'))
.map((file) => file.replace('.mdx', ''));
// サイトマップ XML を生成
const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>${siteUrl}/</loc>
<changefreq>daily</changefreq>
<priority>1.0</priority>
</url>
${posts
.map(
(slug) => `
<url>
<loc>${siteUrl}/posts/${slug}</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>`
)
.join('')}
</urlset>`;
// public ディレクトリに出力
fs.writeFileSync(
path.join(__dirname, '../public/sitemap.xml'),
sitemap
);
console.log('✓ サイトマップを生成しました');
このスクリプトは、すべての記事から自動的にサイトマップを生成します。
package.json
にスクリプトを追加しましょう。
json{
"scripts": {
"dev": "vite",
"build": "vite build && node scripts/generate-sitemap.js",
"preview": "vite preview"
}
}
ビルド時に自動でサイトマップが生成されるようになりました。
RSS フィード生成
scripts/generate-rss.js
で RSS フィードも生成します。
javascriptimport fs from 'fs';
import path from 'path';
import matter from 'gray-matter';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(
fileURLToPath(import.meta.url)
);
const postsDir = path.join(__dirname, '../src/posts');
const siteUrl = 'https://your-blog.com';
const siteTitle = 'My Tech Blog';
const siteDescription = 'Preact で構築した技術ブログ';
RSS フィードに必要な情報を定義します。
次に、各記事のメタデータを読み込んで RSS を生成しましょう。
javascript// 各記事のメタデータを読み込み
const posts = fs
.readdirSync(postsDir)
.filter((file) => file.endsWith('.mdx'))
.map((file) => {
const content = fs.readFileSync(
path.join(postsDir, file),
'utf-8'
);
const { data } = matter(content);
const slug = file.replace('.mdx', '');
return { ...data, slug };
})
.sort((a, b) => new Date(b.date) - new Date(a.date));
// RSS XML を生成
const rss = `<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>${siteTitle}</title>
<link>${siteUrl}</link>
<description>${siteDescription}</description>
<language>ja</language>
<atom:link href="${siteUrl}/rss.xml" rel="self" type="application/rss+xml" />
${posts
.map(
(post) => `
<item>
<title>${post.title}</title>
<link>${siteUrl}/posts/${post.slug}</link>
<description>${post.description}</description>
<pubDate>${new Date(
post.date
).toUTCString()}</pubDate>
<guid>${siteUrl}/posts/${post.slug}</guid>
</item>`
)
.join('')}
</channel>
</rss>`;
fs.writeFileSync(
path.join(__dirname, '../public/rss.xml'),
rss
);
console.log('✓ RSS フィードを生成しました');
gray-matter
を使って各記事の Frontmatter を解析し、RSS アイテムを生成しています。
404 ページの実装
src/pages/NotFound.jsx
でエラーページを作成します。
javascriptimport { h } from 'preact';
import { Link } from 'preact-router/match';
import { Helmet } from 'preact-helmet';
import './NotFound.css';
export default function NotFound() {
return (
<div className='not-found'>
<Helmet>
<title>404 - ページが見つかりません</title>
<meta name='robots' content='noindex' />
</Helmet>
<h1>404</h1>
<p>お探しのページが見つかりませんでした。</p>
<Link href='/' className='home-link'>
ホームに戻る
</Link>
</div>
);
}
SEO を考慮して、404 ページには noindex
を設定しています。
スタイリングの実装
src/index.css
でグローバルスタイルを定義します。
css:root {
--primary-color: #673ab8;
--text-color: #333;
--bg-color: #fff;
--border-color: #e0e0e0;
--code-bg: #f5f5f5;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: -apple-system, BlinkMacSystemFont,
'Segoe UI', sans-serif;
color: var(--text-color);
background-color: var(--bg-color);
line-height: 1.6;
}
main {
max-width: 960px;
margin: 0 auto;
padding: 2rem 1rem;
min-height: calc(100vh - 200px);
}
CSS カスタムプロパティを使うことで、テーマカラーを一元管理しています。
記事詳細ページのスタイルも追加しましょう。
css.article {
max-width: 720px;
margin: 0 auto;
}
.article-header {
margin-bottom: 2rem;
padding-bottom: 1rem;
border-bottom: 2px solid var(--border-color);
}
.article-header h1 {
font-size: 2rem;
margin-bottom: 1rem;
line-height: 1.3;
}
.article-meta {
display: flex;
gap: 1rem;
align-items: center;
color: #666;
font-size: 0.9rem;
}
.article-content {
font-size: 1.05rem;
}
.article-content h2 {
margin-top: 2rem;
margin-bottom: 1rem;
font-size: 1.5rem;
}
.article-content pre {
background-color: var(--code-bg);
padding: 1rem;
border-radius: 4px;
overflow-x: auto;
margin: 1rem 0;
}
.article-content code {
font-family: 'Consolas', 'Monaco', monospace;
font-size: 0.9em;
}
記事本文の可読性を高めるため、適切な行間とフォントサイズを設定しています。
ビルドと公開
開発サーバーで動作確認を行いましょう。
bashyarn dev
ブラウザで http://localhost:5173
を開くと、ブログサイトが表示されます。
問題がなければ、本番用にビルドします。
bashyarn build
dist
ディレクトリに静的ファイルが生成され、サイトマップと RSS フィードも自動生成されます。
Vercel や Netlify などの静的ホスティングサービスにデプロイすれば、すぐに公開できるでしょう。
デプロイ設定(Vercel の例)
Vercel へのデプロイは非常に簡単です。
bash# Vercel CLI をインストール
yarn global add vercel
# プロジェクトルートでデプロイ
vercel
初回デプロイ時にいくつか質問されますが、基本的にデフォルトの設定で問題ありません。
プロジェクトルートに vercel.json
を作成して、細かい設定も可能です。
json{
"buildCommand": "yarn build",
"outputDirectory": "dist",
"devCommand": "yarn dev",
"installCommand": "yarn install",
"framework": "vite",
"rewrites": [
{ "source": "/(.*)", "destination": "/index.html" }
]
}
この設定により、SPA ルーティングが正しく動作するようになります。
パフォーマンス最適化
Preact の軽量性を最大限活かすため、いくつかの最適化を行いましょう。
vite.config.js
にビルド最適化設定を追加します。
javascriptimport { defineConfig } from 'vite';
import preact from '@preact/preset-vite';
import mdx from '@mdx-js/rollup';
import remarkGfm from 'remark-gfm';
import rehypeHighlight from 'rehype-highlight';
export default defineConfig({
plugins: [
mdx({
remarkPlugins: [remarkGfm],
rehypePlugins: [rehypeHighlight],
}),
preact(),
],
build: {
// チャンク分割の最適化
rollupOptions: {
output: {
manualChunks: {
vendor: ['preact', 'preact/hooks'],
router: ['preact-router'],
},
},
},
// 圧縮設定
minify: 'terser',
terserOptions: {
compress: {
drop_console: true, // console.log を削除
},
},
},
});
ベンダーコードを分離することで、キャッシュ効率が向上します。
記事検索機能の追加
ユーザビリティ向上のため、簡易的な記事検索機能を実装しましょう。
src/components/SearchBox.jsx
を作成します。
javascriptimport { h } from 'preact';
import { useState } from 'preact/hooks';
import './SearchBox.css';
export default function SearchBox({ posts, onFilter }) {
const [query, setQuery] = useState('');
const handleSearch = (e) => {
const value = e.target.value;
setQuery(value);
// タイトルまたは説明文で検索
const filtered = posts.filter(
(post) =>
post.title
.toLowerCase()
.includes(value.toLowerCase()) ||
post.description
.toLowerCase()
.includes(value.toLowerCase()) ||
post.tags?.some((tag) =>
tag.toLowerCase().includes(value.toLowerCase())
)
);
onFilter(filtered);
};
return (
<div className='search-box'>
<input
type='search'
placeholder='記事を検索...'
value={query}
onInput={handleSearch}
className='search-input'
/>
</div>
);
}
タイトル、説明文、タグのいずれかに検索キーワードが含まれる記事を抽出します。
src/pages/Home.jsx
に検索ボックスを統合しましょう。
javascriptexport default function Home() {
const [posts, setPosts] = useState([]);
const [filteredPosts, setFilteredPosts] = useState([]);
useEffect(() => {
getAllPosts().then((data) => {
setPosts(data);
setFilteredPosts(data); // 初期表示はすべての記事
});
}, []);
return (
<div className='home'>
<Helmet>
<title>ホーム | My Tech Blog</title>
<meta
name='description'
content='Preact で構築した技術ブログです'
/>
</Helmet>
<h1>最新記事</h1>
<SearchBox
posts={posts}
onFilter={setFilteredPosts}
/>
<div className='articles-grid'>
{filteredPosts.map((post) => (
<ArticleCard key={post.slug} post={post} />
))}
</div>
</div>
);
}
検索結果に応じて表示される記事が動的に変わります。
全体のワークフロー図解
ここまで実装した機能がどのように連携するか、全体像を確認しましょう。
mermaidsequenceDiagram
participant User as 読者
participant Browser as ブラウザ
participant Router as preact-router
participant Component as ページコンポーネント
participant Utils as posts.js
participant MDX as MDX ファイル
User->>Browser: URL アクセス
Browser->>Router: ルーティング処理
Router->>Component: 該当ページをレンダリング
Component->>Utils: 記事データ要求
Utils->>MDX: MDX ファイル読み込み
MDX-->>Utils: frontmatter + Component
Utils-->>Component: 記事データ返却
Component->>Browser: HTML レンダリング
Browser->>User: ページ表示
この図は、ユーザーのアクセスから記事表示までのデータフローを示しています。
Preact のシンプルなアーキテクチャにより、各レイヤーの責務が明確になっていますね。
まとめ
Preact を活用することで、わずか 1 日で本格的なブログサイトを構築できました。
この記事で実装した主要機能を振り返りましょう。
# | 機能 | 実装内容 |
---|---|---|
1 | ルーティング | preact-router で SPA ライクな遷移を実現 |
2 | コンテンツ管理 | MDX で記事を記述し、Frontmatter でメタデータ管理 |
3 | SEO 対策 | preact-helmet でメタタグ、サイトマップ、RSS を自動生成 |
4 | スタイリング | CSS Modules で保守性の高いスタイル管理 |
5 | 検索機能 | クライアントサイドでの簡易記事検索 |
6 | パフォーマンス | Vite + Preact で高速なビルドと軽量な成果物 |
Preact の軽量性と React 互換性により、学習コストを抑えながら高パフォーマンスなブログを実現できます。さらに拡張するなら、コメント機能やタグページ、ダークモード対応なども検討できるでしょう。
この構成をベースに、あなただけの技術ブログを今日から始めてみてはいかがでしょうか。
関連リンク
- article
Preact でミニブログを 1 日で公開:ルーティング・MDX・SEO まで一気通貫
- article
Preact でスケーラブルな状態管理:Signals/Context/外部ストアの責務分離
- article
Preact チートシート【保存版】:JSX/Props/Events/Ref の書き方早見表
- article
Vite で始める Preact:公式プラグイン設定と最短プロジェクト作成【完全手順】
- article
【徹底比較】Preact vs React 2025:バンドル・FPS・メモリ・DX を総合評価
- article
【2025 年最新版】Preact の強みと限界を実測で俯瞰:軽量・高速・互換性の現在地
- article
shadcn/ui で Command Palette を実装:検索・履歴・キーボードショートカット対応
- article
GPT-5 本番運用の SLO 設計:品質(正確性/再現性)・遅延・コストの三点均衡を保つ
- article
Emotion の「変種(variants)」設計パターン:props→ スタイルの型安全マッピング
- article
Remix でブログをゼロから構築:Markdown・検索・タグ・OGP まで実装
- article
Preact でミニブログを 1 日で公開:ルーティング・MDX・SEO まで一気通貫
- article
Electron スクリーンレコーダー/キャプチャツールを desktopCapturer で作る
- blog
iPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
- blog
Googleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
- blog
【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
- blog
Googleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
- blog
Pixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
- blog
フロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
- review
今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
- review
ついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
- review
愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
- review
週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
- review
新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
- review
科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来