概要
astro-notion-blogをベースにしたブログサイトに、Aboutページやプライバシーポリシーなどの固定ページを追加する方法を解説します。
Notionのデータベース内の記事を固定ページとして利用しつつ、記事一覧には表示しない仕組みを実装します。
要件
-
/about、/privacy-policyなどのURLで固定ページにアクセスできる - 固定ページはNotionで管理(通常の記事と同じワークフロー)
- 記事一覧やサイドバーには固定ページを表示しない
- ヘッダーナビゲーションから固定ページにアクセスできる
実装方針
Slug方式を採用
環境変数でPage IDを指定する方式ではなく、Slugでフィルタする方式を採用しました。
| 観点 | 環境変数方式 | Slug方式 |
|---|---|---|
| シンプルさ | △ 環境変数管理が必要 | ◎ コードで完結 |
| 管理の一貫性 | △ 別管理になる | ◎ 他の記事と同じ |
| Notion側の操作 | △ Page ID調べる必要 | ◎ Slugを設定するだけ |
ファイル構成
src/
├── lib/
│ └── blog-helpers.ts # 除外Slugリストとフィルタ関数を追加
├── layouts/
│ └── Layout.astro # ヘッダーナビゲーションにリンク追加
└── pages/
├── about.astro # 新規作成
├── privacy-policy.astro # 新規作成
├── index.astro # フィルタ適用
└── posts/
├── [slug].astro # フィルタ適用
├── page/[page].astro # フィルタ適用
└── tag/
├── [tag].astro # フィルタ適用
└── [tag]/page/[page].astro # フィルタ適用
実装手順
1. 除外Slugリストとフィルタ関数の定義
src/lib/blog-helpers.tsに追加:
import type { Post } from './interfaces'
// 記事一覧から除外する固定ページのSlugリスト
export const EXCLUDED_SLUGS = ['about', 'privacy-policy']
// 固定ページを除外するフィルタ関数
export const filterExcludedPosts = (posts: Post[]): Post[] => {
return posts.filter((post) => !EXCLUDED_SLUGS.includes(post.Slug))
}
2. 固定ページの作成
src/pages/about.astroを新規作成:
---
import {
getPosts,
getRankedPosts,
getPostBySlug,
getBlock,
getAllTags,
getAllBlocksByBlockId,
downloadFile,
} from '../lib/notion/client.ts'
import { filePath, extractTargetBlocks, filterExcludedPosts } from '../lib/blog-helpers.ts'
import Layout from '../layouts/Layout.astro'
import PostBody from '../components/PostBody.astro'
import BlogPostsLink from '../components/BlogPostsLink.astro'
import BlogTagsLink from '../components/BlogTagsLink.astro'
import styles from '../styles/blog.module.css'
const post = await getPostBySlug('about')
if (!post) {
throw new Error('About page not found. Please create a post with slug "about" in Notion.')
}
const [blocks, rankedPostsRaw, recentPostsRaw, tags] = await Promise.all([
getAllBlocksByBlockId(post.PageId),
getRankedPosts(),
getPosts(5),
getAllTags(),
])
const rankedPosts = filterExcludedPosts(rankedPostsRaw)
const recentPosts = filterExcludedPosts(recentPostsRaw)
// 画像ダウンロード処理(省略)
---
<Layout
title={post.Title}
description={post.Excerpt}
path="/about"
ogImage={ogImage}
>
<div slot="main" class={styles.main}>
<div class={styles.post}>
<PostBody blocks={blocks} />
</div>
</div>
<div slot="aside" class="aside">
<BlogPostsLink heading="Recommended" posts={rankedPosts} />
<BlogPostsLink heading="Latest posts" posts={recentPosts} />
<BlogTagsLink heading="Categories" tags={tags} />
</div>
</Layout>
3. 記事一覧へのフィルタ適用
src/pages/index.astroなどでフィルタを適用:
---
import { filterExcludedPosts } from '../lib/blog-helpers.ts'
const [allPosts, allRankedPosts, tags, numberOfPages] = await Promise.all([
getPosts(NUMBER_OF_POSTS_PER_PAGE),
getRankedPosts(),
getAllTags(),
getNumberOfPages(),
])
// 固定ページを除外
const posts = filterExcludedPosts(allPosts)
const rankedPosts = filterExcludedPosts(allRankedPosts)
---
4. ヘッダーナビゲーションにリンク追加
src/layouts/Layout.astroのヘッダーナビゲーションに追加:
<a href={getNavLink('/about')} class="nav-item">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
<circle cx="12" cy="7" r="4"></circle>
</svg>
<span>About</span>
</a>
Notion側の設定
- データベースで新しいページを作成
- Slugプロパティを
aboutまたはprivacy-policyに設定 - Publishedプロパティにチェック
- Dateプロパティに今日以前の日付を設定
拡張性
新しい固定ページを追加する場合:
-
EXCLUDED_SLUGS配列に新しいSlugを追加 - 対応する
.astroファイルを作成 - Notionで該当Slugの記事を作成
export const EXCLUDED_SLUGS = ['about', 'privacy-policy', 'terms', 'contact']
まとめ
Slug方式を採用することで、以下のメリットが得られます:
- Notion側での管理が簡単(Slugを設定するだけ)
- 環境変数の管理が不要
- 他の記事と同じワークフローで編集可能
- フィルタ関数の再利用で保守性が向上