コンテンツへスキップ
Fumadocs

ブログ

アプリにブログを追加します。

インストール

npx @fumadocs/cli@latest add blog

コンポーネントは components/blog に、投稿用のユーティリティは lib/blog.ts にインストールされます。

blogPageSchema を使って投稿用のコレクションを定義し、著者を登録します。

import { defineConfig } from 'astro/config';
import fumadocs from '@fumadocs/astro';

export default defineConfig({
  integrations: [
    fumadocs({
      components: './src/components/mdx.tsx',
      collections: {
        docs: { dir: 'content/docs' },
        blog: { dir: 'content/blog' },
      },
    }),
  ],
});

投稿は content/blog 配下に作成します。

content/blog/hello-world.mdx
---
title: Hello World
description: My first post.
date: 2026-10-12
tags: [Announcement]
authors: [fuma]
image: /blog/hello-world.png
---
フィールド説明
date公開日です。投稿はこの日付順に並べ替えられます
tagsタグごとに /blog/tags 配下にページが作成されます
authors著者のキーです。未登録のキーは著者名として扱われます
imageカバー画像の URL です。投稿のカードに表示されます

ページ

getPosts() は、ページの著者とタグを解決したうえで、新しい順に返します。返されたページは、Home Layout 配下のルートでレンダリングしてください。

投稿

---
import Layout from '@/layouts/Layout.astro';
import { BlogPosts } from '@/components/blog-posts';
import { blogAuthors, blogSource } from '@/lib/source';
import { getPosts } from '@/lib/blog';
---

<Layout title="Blog">
  <BlogPosts
    client:load
    pathname={Astro.url.pathname}
    params={Astro.params}
    posts={getPosts(blogSource.getPages(), blogAuthors)}
  />
</Layout>

投稿

findPost() は、投稿とその前後の投稿 (新しい投稿と古い投稿) をまとめて取得します。toc を指定すると、目次と共有ボタンを含むフローティングパネルが表示されます。

---
import type { GetStaticPaths, InferGetStaticPropsType } from 'astro';
import Layout from '@/layouts/Layout.astro';
import { BlogPostPage } from '@/components/blog-post';
import { blogAuthors, blogSource } from '@/lib/source';
import { findPost, getPosts } from '@/lib/blog';

export const getStaticPaths = (() => {
  const posts = getPosts(blogSource.getPages(), blogAuthors);

  return blogSource.getPages().map((page) => ({
    params: { slug: decodeURI(page.slugs[0]) },
    props: { page, ...findPost(posts, page.url)! },
  }));
}) satisfies GetStaticPaths;

type Props = InferGetStaticPropsType<typeof getStaticPaths>;

const { page, post, newer, older } = Astro.props;
const { body: Content, toc } = await page.data.load();
---

<Layout title={post.title} description={post.description}>
  <BlogPostPage
    client:load
    pathname={Astro.url.pathname}
    params={Astro.params}
    post={post}
    newer={newer}
    older={older}
    toc={toc}
  >
    <Content />
  </BlogPostPage>
</Layout>

タグ

getTags() を使うと、すべてのタグを一覧表示できます。

---
import Layout from '@/layouts/Layout.astro';
import { BlogTagsPage } from '@/components/blog-tags';
import { blogSource } from '@/lib/source';
import { getPosts, getTags } from '@/lib/blog';
---

<Layout title="All Tags">
  <BlogTagsPage
    client:load
    pathname={Astro.url.pathname}
    params={Astro.params}
    tags={getTags(getPosts(blogSource.getPages()))}
  />
</Layout>

また、特定のタグが付いた投稿は getTagPosts() で表示できます。存在しないタグの場合、この関数は undefined を返します。

---
import type { GetStaticPaths, InferGetStaticPropsType } from 'astro';
import Layout from '@/layouts/Layout.astro';
import { BlogTagPage } from '@/components/blog-tag';
import { blogAuthors, blogSource } from '@/lib/source';
import { getPosts, getTagPosts, getTags } from '@/lib/blog';

export const getStaticPaths = (() => {
  const posts = getPosts(blogSource.getPages(), blogAuthors);

  return getTags(posts).map((tag) => ({
    params: { tag: tag.slug },
    props: getTagPosts(posts, tag.slug)!,
  }));
}) satisfies GetStaticPaths;

type Props = InferGetStaticPropsType<typeof getStaticPaths>;

const { tag, posts } = Astro.props;
---

<Layout title={tag.name}>
  <BlogTagPage
    client:load
    pathname={Astro.url.pathname}
    params={Astro.params}
    tag={tag}
    posts={posts}
  />
</Layout>

国際化

getPosts() には、blogSource.getPages(lang) のように特定のロケールのページを渡してください。

タグページのURLは lib/blog.ts の getBlogUrl によって生成されます。ローダーのURLと一致させるには、getBlogUrl に i18n の設定を渡してください。

lib/blog.ts
import { i18n } from '@/lib/i18n';

export const getBlogUrl = createGetUrl('/blog', i18n);

投稿のフィードを公開する方法については、RSS フィードを参照してください。

How is this guide?

最終更新日