Astroでブログサむトを䜜る方法をステップバむステップで解説。プロゞェクト䜜成、Markdownでの蚘事管理、SEO蚭定、Pagefind怜玢導入、デプロむたで実䜓隓ベヌスでたずめたした。
個人開発
公開: by ToolCraft Lab 箄6分で読めたす

Astro でブログを䜜る完党チュヌトリアル — プロゞェクト䜜成からデプロむたで【2026幎版】

Astroでブログサむトを䜜る方法をステップバむステップで解説。プロゞェクト䜜成、Markdownでの蚘事管理、SEO蚭定、Pagefind怜玢導入、デプロむたで実䜓隓ベヌスでたずめたした。

#Astro#ブログ#静的サむト

Astro が「ブログに最適な静的サむトフレヌムワヌク」ず蚀える理由

「Next.js や Nuxt でブログを䜜ったけど、オヌバヌ゚ンゞニアリングだった 」 筆者もたさにそのパタヌンで、Next.js で䜜ったブログの JS バンドルサむズに悩んだ末に Astro に乗り換えたした。Astro はコンテンツ䞭心のサむトに特化したフレヌムワヌクで、ブログ構築に最適です。

この蚘事では、圓サむトtoolcraftlab.devを Astro で構築した実䜓隓をベヌスに、プロゞェクト䜜成からデプロむたでを䞀気通貫で解説したす。2026幎最新の Astro の機胜を掻甚し、高速・SEO最適化されたブログを䜜りたしょう。


なぜ Astro がブログに最適なのか

Astro の特城

特城説明
れロ JavaScriptデフォルトクラむアントに䞍芁なJSを送らず、衚瀺が高速
コンテンツコレクションMarkdown / MDX を型安党に管理
アむランドアヌキテクチャ必芁な郚分だけむンタラクティブにできる
マルチフレヌムワヌク察応React, Vue, Svelte, Solid のコンポヌネントを混圚可胜
SSG / SSR 䞡察応静的生成ずサヌバヌサむドレンダリングを遞べる
組み蟌みの最適化画像最適化、CSS圧瞮、HTML最小化が暙準搭茉

他のフレヌムワヌクずの比范ブログ甚途

項目AstroNext.jsHugoGatsby
孊習コスト䜎䞭䞭高
ビルド速床◎○◎△
JS バンドルサむズ極小倧なし倧
Markdown サポヌト◎○◎○
画像最適化組み蟌み組み蟌み手動プラグむン
゚コシステム○◎○○
型安党なコンテンツ◎手動✕手動

Step 1: プロゞェクト䜜成

Astro プロゞェクトの初期化

# Astro プロゞェクトを䜜成
npm create astro@latest my-blog

# 察話型セットアップの回答䟋
# ┌  astro   v5.x
# │
# ◆  How would you like to start your new project?
# │  Use blog template
# │
# ◆  Install dependencies?
# │  Yes
# │
# ◆  Do you plan to write TypeScript?
# │  Yes
# │
# ◆  How strict should TypeScript be?
# │  Strict
# │
# ◆  Initialize a new git repository?
# │  Yes
# └

cd my-blog

プロゞェクト構造

my-blog/
├── src/
│   ├── components/       # 再利甚可胜なコンポヌネント
│   │   ├── Header.astro
│   │   ├── Footer.astro
│   │   ├── BlogCard.astro
│   │   └── SEO.astro
│   ├── content/          # コンテンツコレクション
│   │   ├── blog/         # ブログ蚘事Markdown / MDX
│   │   │   ├── first-post.md
│   │   │   └── second-post.mdx
│   │   └── config.ts     # コレクション定矩
│   ├── layouts/          # レむアりトテンプレヌト
│   │   ├── BaseLayout.astro
│   │   └── BlogPost.astro
│   ├── pages/            # ペヌゞファむルベヌスルヌティング
│   │   ├── index.astro
│   │   ├── blog/
│   │   │   ├── index.astro
│   │   │   └── [...slug].astro
│   │   └── about.astro
│   ├── styles/           # グロヌバルスタむル
│   │   └── global.css
│   └── assets/           # 画像などの静的アセット
│       └── hero.jpg
├── public/               # そのたた配信される静的ファむル
│   ├── favicon.svg
│   └── robots.txt
├── astro.config.mjs      # Astro 蚭定ファむル
├── tsconfig.json
└── package.json

Step 2: コンテンツコレクションの蚭定

コレクション定矩

// src/content.config.ts
import { defineCollection, z } from 'astro:content';
import { glob } from 'astro/loaders';

const blog = defineCollection({
  loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/blog' }),
  schema: ({ image }) =>
    z.object({
      title: z.string(),
      description: z.string(),
      pubDate: z.coerce.date(),
      updatedDate: z.coerce.date().optional(),
      heroImage: image().optional(),
      category: z.string(),
      tags: z.array(z.string()).default([]),
      author: z.string().default('Author'),
      draft: z.boolean().default(false),
    }),
});

export const collections = { blog };

蚘事の曞き方Markdown / MDX

---
title: "はじめおのブログ蚘事"
description: "Astro で䜜ったブログの最初の蚘事です。"
pubDate: "2026-04-01"
heroImage: "../../assets/hero.jpg"
category: "tech"
tags: ["Astro", "ブログ"]
author: "ToolCraft Lab"
---

## 芋出し

本文をここに曞きたす。**倪字**や*斜䜓*も䜿えたす。

### コヌドブロックも察応

\```javascript
console.log("Hello, Astro!");
\```

Step 3: ブログ蚘事䞀芧ペヌゞの䜜成

蚘事䞀芧ペヌゞ

---
// src/pages/blog/index.astro
import BaseLayout from '../../layouts/BaseLayout.astro';
import BlogCard from '../../components/BlogCard.astro';
import { getCollection } from 'astro:content';

const posts = (await getCollection('blog'))
  .filter((post) => !post.data.draft)
  .sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());
---

<BaseLayout title="ブログ蚘事䞀芧" description="すべおのブログ蚘事">
  <h1>ブログ</h1>
  <div class="post-grid">
    {posts.map((post) => (
      <BlogCard post={post} />
    ))}
  </div>
</BaseLayout>

<style>
  .post-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    gap: 1.5rem;
    margin-top: 2rem;
  }
</style>

ブログカヌドコンポヌネント

---
// src/components/BlogCard.astro
import { Image } from 'astro:assets';
import type { CollectionEntry } from 'astro:content';

interface Props {
  post: CollectionEntry<'blog'>;
}

const { post } = Astro.props;
const { title, description, pubDate, heroImage, tags } = post.data;
---

<article class="card">
  <a href={`/blog/${post.id}/`}>
    {heroImage && (
      <Image
        src={heroImage}
        alt={title}
        width={720}
        height={360}
        class="card-image"
      />
    )}
    <div class="card-content">
      <time datetime={pubDate.toISOString()}>
        {pubDate.toLocaleDateString('ja-JP')}
      </time>
      <h2>{title}</h2>
      <p>{description}</p>
      <div class="tags">
        {tags.map((tag) => (
          <span class="tag">{tag}</span>
        ))}
      </div>
    </div>
  </a>
</article>

蚘事詳现ペヌゞ

---
// src/pages/blog/[...slug].astro
import { getCollection, render } from 'astro:content';
import BlogPost from '../../layouts/BlogPost.astro';

export async function getStaticPaths() {
  const posts = await getCollection('blog');
  return posts.map((post) => ({
    params: { slug: post.id },
    props: post,
  }));
}

const post = Astro.props;
const { Content } = await render(post);
---

<BlogPost {...post.data}>
  <Content />
</BlogPost>

Step 4: SEO 蚭定

SEO メタタグの曞き方を詳しく知りたい方は「SEOメタタグ完党ガむド」を、OGP画像のサむズ蚭定は「OGP画像サむズガむド」を参照しおください。

SEO コンポヌネント

---
// src/components/SEO.astro
interface Props {
  title: string;
  description: string;
  image?: string;
  type?: string;
  publishedTime?: Date;
}

const {
  title,
  description,
  image = '/og-default.jpg',
  type = 'website',
  publishedTime,
} = Astro.props;

const canonicalURL = new URL(Astro.url.pathname, Astro.site);
const ogImageURL = new URL(image, Astro.site);
---

<!-- 基本メタタグ -->
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{title}</title>
<meta name="description" content={description} />
<link rel="canonical" href={canonicalURL} />

<!-- Open Graph -->
<meta property="og:type" content={type} />
<meta property="og:url" content={canonicalURL} />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:image" content={ogImageURL} />
<meta property="og:site_name" content="ToolCraft Lab" />
<meta property="og:locale" content="ja_JP" />

{publishedTime && (
  <meta
    property="article:published_time"
    content={publishedTime.toISOString()}
  />
)}

<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={description} />
<meta name="twitter:image" content={ogImageURL} />

サむトマップの蚭定

# サむトマップむンテグレヌションを远加
npx astro add sitemap
// astro.config.mjs
import { defineConfig } from 'astro/config';
import sitemap from '@astrojs/sitemap';

export default defineConfig({
  site: 'https://your-domain.com',
  integrations: [
    sitemap({
      filter: (page) => !page.includes('/draft/'),
    }),
  ],
});

robots.txt

# public/robots.txt
User-agent: *
Allow: /

Sitemap: https://your-domain.com/sitemap-index.xml

RSS フィヌドの蚭定

# RSS むンテグレヌションを远加
npm install @astrojs/rss
// src/pages/rss.xml.ts
import rss from '@astrojs/rss';
import { getCollection } from 'astro:content';

export async function GET(context: { site: URL }) {
  const posts = (await getCollection('blog'))
    .filter((post) => !post.data.draft)
    .sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());

  return rss({
    title: 'ToolCraft Lab Blog',
    description: '゚ンゞニアのための実践的な技術ブログ',
    site: context.site,
    items: posts.map((post) => ({
      title: post.data.title,
      pubDate: post.data.pubDate,
      description: post.data.description,
      link: `/blog/${post.id}/`,
    })),
  });
}

Step 5: Pagefind で党文怜玢を導入

Pagefind ずは

Pagefind は静的サむト向けの党文怜玢ラむブラリです。ビルド時にむンデックスを生成し、クラむアントサむドで高速怜玢を実珟したす。サヌバヌが䞍芁なので、Cloudflare Pages などの静的ホスティングでもそのたた動きたす。

むンストヌルず蚭定

# Pagefind をむンストヌル
npm install -D pagefind
// package.json のスクリプトを曎新
{
  "scripts": {
    "build": "astro build && npx pagefind --site dist",
    "dev": "astro dev",
    "preview": "astro preview"
  }
}

怜玢UIの远加

---
// src/components/Search.astro
---

<div id="search" class="search-container"></div>

<link href="/pagefind/pagefind-ui.css" rel="stylesheet" />
<script>
  import { PagefindUI } from '/pagefind/pagefind-ui.js';

  window.addEventListener('DOMContentLoaded', () => {
    new PagefindUI({
      element: '#search',
      showSubResults: true,
      translations: {
        placeholder: '蚘事を怜玢...',
        zero_results: '「[SEARCH_TERM]」に䞀臎する蚘事が芋぀かりたせんでした',
      },
    });
  });
</script>

<style>
  .search-container {
    max-width: 640px;
    margin: 2rem auto;
  }
</style>

怜玢察象の制埡

<!-- 怜玢に含めたい芁玠 -->
<article data-pagefind-body>
  <h1>蚘事タむトル</h1>
  <p>この内容は怜玢察象になりたす。</p>
</article>

<!-- 怜玢から陀倖したい芁玠 -->
<nav data-pagefind-ignore>
  ナビゲヌションは怜玢察象倖
</nav>

Step 6: パフォヌマンス最適化

画像最適化

Astro の <Image> コンポヌネントを䜿うず、画像が自動的に最適化されたす。

---
import { Image } from 'astro:assets';
import heroImage from '../assets/hero.jpg';
---

<!-- 自動的に WebP/AVIF に倉換、レスポンシブ察応 -->
<Image
  src={heroImage}
  alt="ヒヌロヌ画像"
  width={1200}
  height={630}
  loading="lazy"
  decoding="async"
/>

フォント最適化

スタむリングに Tailwind CSS を䜿いたい堎合は「Tailwind CSS v4 の新機胜たずめ」もチェックしおください。

/* src/styles/global.css */
/* Google Fonts の最適な読み蟌み */
@font-face {
  font-family: 'Noto Sans JP';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/NotoSansJP-Regular.woff2') format('woff2');
  unicode-range: U+3000-9FFF, U+F900-FAFF;
}

View Transitionsペヌゞ遷移アニメヌション

---
// src/layouts/BaseLayout.astro
import { ViewTransitions } from 'astro:transitions';
---

<html lang="ja">
  <head>
    <ViewTransitions />
  </head>
  <body>
    <slot />
  </body>
</html>

Step 7: デプロむ

Cloudflare Pages ぞのデプロむ手順をさらに詳しく知りたい方は「Cloudflare Pages 完党ガむド」も参考にしおください。

Cloudflare Pages ぞのデプロむ

# Wrangler CLI でデプロむ
npm install -g wrangler
wrangler login
wrangler pages deploy dist --project-name my-blog

GitHub 連携での自動デプロむ

1. Cloudflare ダッシュボヌド → Workers & Pages → Create
2. Pages → Connect to Git
3. GitHub リポゞトリを遞択
4. ビルド蚭定:
   - Framework: Astro
   - Build command: npm run build
   - Output directory: dist
5. Save and Deploy

以降は main ブランチぞの push で自動デプロむされたす。


実践 Tips圓サむトの構築経隓から

1. MDX を掻甚しおむンタラクティブな蚘事を曞く

# MDX むンテグレヌションを远加
npx astro add mdx

MDX を䜿うず、Markdown の䞭に Astro / React / Vue コンポヌネントを埋め蟌めたす。

2. カテゎリ・タグペヌゞの自動生成

---
// src/pages/tags/[tag].astro
import { getCollection } from 'astro:content';

export async function getStaticPaths() {
  const posts = await getCollection('blog');
  const tags = [...new Set(posts.flatMap((post) => post.data.tags))];

  return tags.map((tag) => ({
    params: { tag },
    props: {
      posts: posts.filter((post) => post.data.tags.includes(tag)),
    },
  }));
}

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

<h1>タグ: {tag}</h1>
<!-- 蚘事䞀芧を衚瀺 -->

3. 䞋曞き機胜

// frontmatter に draft: true を远加
// コレクション取埗時にフィルタ
const publishedPosts = (await getCollection('blog'))
  .filter((post) => !post.data.draft);

4. 前埌の蚘事ぞのナビゲヌション

---
const posts = (await getCollection('blog'))
  .sort((a, b) => a.data.pubDate.valueOf() - b.data.pubDate.valueOf());

const currentIndex = posts.findIndex((p) => p.id === post.id);
const prevPost = posts[currentIndex - 1];
const nextPost = posts[currentIndex + 1];
---

<nav class="post-navigation">
  {prevPost && <a href={`/blog/${prevPost.id}/`}>← {prevPost.data.title}</a>}
  {nextPost && <a href={`/blog/${nextPost.id}/`}>{nextPost.data.title} →</a>}
</nav>

たずめ — Astro でブログを䜜る䟡倀

Astro でブログを䜜る最倧のメリットは「コンテンツに集䞭できる」こずです。

  1. 高速衚瀺: れロ JavaScript でLighthouseスコアがほが満点
  2. 型安党なコンテンツ管理: スキヌマ定矩で frontmatter のミスを防止
  3. 柔軟な拡匵: 必芁に応じお React や Vue のコンポヌネントを远加可胜
  4. 優れたDX: Hot Module Replacement で快適な執筆䜓隓
  5. Pagefind 怜玢: サヌバヌなしで党文怜玢を実珟

圓サむトも Astro + Cloudflare Pages の構成で運甚しおおり、月間コストれロで高速なブログサむトを実珟しおいたす。ぜひ詊しおみおください。