Astroを使ってみよう - コンテンツ重視サイトを高速に作る入門

beginner | 65分 で読める | 2026.04.10

公式ドキュメント

この記事の要点

Astroの島アーキテクチャでJS最小化の高速サイトを構築する
• コンテンツコレクションでMarkdownを型安全に管理する
• ファイルベースルーティングとレイアウトで効率的にページを作る

このチュートリアルで学ぶこと

  • Astroプロジェクトの作成と基本構造
  • .astroコンポーネントの書き方
  • レイアウトとスロット
  • ルーティングとページ
  • Markdownとコンテンツコレクション
  • Tailwind CSSの統合
  • 本番ビルドとデプロイ準備

Astroはコンテンツ中心のWebサイトを爆速で配信するための静的サイトジェネレータです。デフォルトでJavaScriptを最小化し、必要な部分だけインタラクティブにする「島アーキテクチャ」が特徴です。

前提条件・必要な環境

  • Node.js 18.20.8、20.3.0、22.0.0 以上
  • npm 10以上 もしくは pnpm
  • HTML / CSS / JavaScriptの基本知識
  • ターミナルとエディタ(VS Code推奨)

確認:

node -v
npm -v

インストール / セットアップ

実践メモ: npm create astro@latestで対話的にプロジェクトを作成できます。テンプレートは「Empty」から始めるのがおすすめです。

公式CLIで対話的に作成できます。

npm create astro@latest my-blog

選択肢の例:

  • Template: Empty
  • TypeScript: Strict
  • Install dependencies: Yes
  • Initialize git: Yes

完了後、開発サーバーを起動します。

cd my-blog
npm run dev

ブラウザで http://localhost:4321 にアクセスすると、Astroのスタートページが見えます。

ディレクトリ構成

my-blog/
├── public/
├── src/
│   ├── components/
│   ├── layouts/
│   ├── pages/
│   └── content/
├── astro.config.mjs
├── tsconfig.json
└── package.json
  • src/pages/ 配下のファイルがそのままURLになります
  • src/components/ は再利用するUI部品
  • src/layouts/ は共通レイアウト
  • src/content/ はコンテンツコレクション(後述)

基本概念

.astroコンポーネント

ポイント: .astroファイルの---で囲まれた部分はサーバー側で実行されるため、APIフェッチやファイル読み込みも自由に行えます。

--- で囲まれたフロントマターにJavaScript / TypeScript、その下にHTMLを書きます。

---
const name = "Astro";
---
<h1>Hello, {name}!</h1>

サーバー側で実行されるため、ファイル読み込みやfetchも自由に行えます。

島アーキテクチャ

デフォルトでは静的HTMLとして出力。Reactなどのコンポーネントを使うときは client:load などの指示子を付けたときだけJSが配信されます。

ファイルベースルーティング

src/pages/about.astro/aboutsrc/pages/blog/[slug].astro で動的ルートになります。

ステップバイステップ実装

シンプルなブログサイトを作っていきます。

Step 1: レイアウトを作る

src/layouts/BaseLayout.astro:

---
interface Props {
  title: string;
  description?: string;
}
const { title, description = "My Astro Blog" } = Astro.props;
---
<!doctype html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="description" content={description} />
    <title>{title}</title>
  </head>
  <body>
    <header>
      <nav>
        <a href="/">Home</a> |
        <a href="/blog">Blog</a> |
        <a href="/about">About</a>
      </nav>
    </header>
    <main>
      <slot />
    </main>
    <footer>
      <p>&copy; 2026 My Astro Blog</p>
    </footer>
  </body>
</html>

<slot /> の位置に子コンポーネントの中身が入ります。これがレイアウトの核心です。

Step 2: トップページ

src/pages/index.astro:

---
import BaseLayout from '../layouts/BaseLayout.astro';
---
<BaseLayout title="Home">
  <h1>Welcome to My Blog</h1>
  <p>Astroで作った静的サイトの例です。</p>
</BaseLayout>

Step 3: コンポーネントを切り出す

src/components/Card.astro:

---
interface Props {
  title: string;
  href: string;
  body: string;
}
const { title, href, body } = Astro.props;
---
<a href={href} class="card">
  <h2>{title}</h2>
  <p>{body}</p>
</a>

<style>
  .card {
    display: block;
    padding: 1rem;
    border: 1px solid #ddd;
    border-radius: 8px;
    text-decoration: none;
    color: inherit;
  }
  .card:hover {
    background: #f5f5f5;
  }
</style>

ポイント: <style>はそのコンポーネントに自動スコープされるため、スタイルの衝突を気にする必要がありません。

Step 4: コンテンツコレクションをセットアップ

実践メモ: content.config.tsでZodスキーマを定義すると、frontmatterの型チェックが自動で行われます。

src/content.config.ts を作成 (Astro 5 以降):

import { defineCollection, z } from 'astro:content';
import { glob } from 'astro/loaders';

const blog = defineCollection({
  loader: glob({ pattern: '**/*.md', base: './src/content/blog' }),
  schema: z.object({
    title: z.string(),
    description: z.string(),
    pubDate: z.coerce.date(),
    tags: z.array(z.string()).optional(),
  }),
});

export const collections = { blog };

Step 5: Markdown記事を追加

src/content/blog/hello-astro.md:

---
title: "Hello Astro"
description: "Astroで初めて書いた記事"
pubDate: 2026-04-01
tags: ["astro", "introduction"]
---

# Hello Astro

これは **Markdown** で書いた記事です。

- 簡単
- 高速
- 楽しい

src/content/blog/island-architecture.md も同様に追加してみてください。

Step 6: 記事一覧ページ

src/pages/blog/index.astro:

---
import { getCollection } from 'astro:content';
import BaseLayout from '../../layouts/BaseLayout.astro';
import Card from '../../components/Card.astro';

const posts = (await getCollection('blog')).sort(
  (a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(),
);
---
<BaseLayout title="Blog">
  <h1>記事一覧</h1>
  <ul>
    {posts.map((post) => (
      <li>
        <Card
          title={post.data.title}
          href={`/blog/${post.id}`}
          body={post.data.description}
        />
      </li>
    ))}
  </ul>
</BaseLayout>

Step 7: 記事詳細ページ(動的ルート)

src/pages/blog/[...slug].astro:

---
import { getCollection, render } from 'astro:content';
import BaseLayout from '../../layouts/BaseLayout.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);
---
<BaseLayout title={post.data.title} description={post.data.description}>
  <article>
    <h1>{post.data.title}</h1>
    <time datetime={post.data.pubDate.toISOString()}>
      {post.data.pubDate.toLocaleDateString('ja-JP')}
    </time>
    <Content />
  </article>
</BaseLayout>

注意: 動的ルート([slug].astro)では必ずgetStaticPathsをエクスポートしてください。忘れるとビルドエラーになります。

Step 8: Tailwind CSSを追加 (任意)

npx astro add tailwind

設定ファイルが自動更新されます。src/styles/global.css をimportして使用します。

Step 9: ビルド

npm run build
npm run preview

dist/ に静的ファイルが出力されます。これをそのままNetlify、Vercel、Cloudflare Pagesなどにデプロイできます。

完成コード全体

my-blog/
├── public/
│   └── favicon.svg
├── src/
│   ├── components/
│   │   └── Card.astro
│   ├── content/
│   │   └── blog/
│   │       ├── hello-astro.md
│   │       └── island-architecture.md
│   ├── layouts/
│   │   └── BaseLayout.astro
│   ├── pages/
│   │   ├── blog/
│   │   │   ├── [...slug].astro
│   │   │   └── index.astro
│   │   ├── about.astro
│   │   └── index.astro
│   └── content.config.ts
├── astro.config.mjs
├── package.json
└── tsconfig.json

各ファイルは前のステップのコードで動作確認できます。

よくあるエラーと対処

Cannot find module 'astro:content'

注意: Astroの自動生成型がまだ作られていない場合に発生します。npx astro syncを実行するか、開発サーバーを再起動してください。

Astroの自動生成型がまだ作られていない可能性があります。一度開発サーバーを再起動するか、

npx astro sync

を実行してください。

getStaticPaths() is required for dynamic routes

動的ルート([slug].astro)では必ず getStaticPaths をエクスポートします。SSRモードでは prerender = false に切り替えることもできます。

Markdownのfrontmatterスキーマ違反

Zodスキーマと一致しないと開発サーバーがエラーを出します。日付は ISO 形式 (2026-04-01) を使うのが確実です。

<style> がグローバルに漏れる

.astro<style> は自動でスコープされますが、<style is:global> を使うと意図的にグローバルになります。意図せず使っていないか確認しましょう。

ベストプラクティス

  • デフォルトはサーバーレンダリング: JSが必要なときだけクライアント指示子(client:load/client:idle/client:visible)を使う
  • コンテンツはコレクションに: 型安全になり、frontmatterのミスを早期に発見できる
  • 画像は <Image /> コンポーネント: 自動最適化が効く (astro:assets)
  • 公式インテグレーションを活用: npx astro add <name> でTailwindやMDXなどを安全に追加できる
  • ページレベルのSEO: layoutでmetaタグを受け取れるようpropsで制御
  • Lighthouseで計測: Astroの強みは速度。CIに組み込んでスコアを保つ

次のステップ(発展課題)

  1. MDXを導入してJSXコンポーネントを記事中で使う
  2. RSSフィード@astrojs/rss で生成
  3. サイトマップ@astrojs/sitemap で出力
  4. Reactアイランドを試す: 検索フォームだけClient-side renderingにしてみる
  5. View Transitions API: ページ遷移を滑らかに
  6. Cloudflare Pagesにデプロイ: GitHub連携で自動ビルド
  7. i18n対応: 多言語ブログ化

まとめ

Astroは「速い」「シンプル」「コンテンツ中心」の三拍子が揃ったフレームワークです。

  • ファイルベースルーティングで学習コストが低い
  • コンテンツコレクションでMarkdownを型安全に扱える
  • JS最小化で抜群のパフォーマンス
  • 必要に応じてReact/Vue/Svelteを混ぜられる柔軟さ

ブログ、ドキュメントサイト、マーケティングLPなど、コンテンツ重視のサイトを作るならAstroは第一候補になるでしょう。

補足: クライアントコンポーネントの統合

Astroは公式インテグレーションでReact / Vue / Svelte / Solid / Preactなどを混ぜて使えます。例えばReactを追加する場合:

npx astro add react

src/components/Counter.tsx:

import { useState } from 'react';

export default function Counter() {
  const [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount(count + 1)}>
      Count: {count}
    </button>
  );
}

.astro から呼び出すときに client directive を付けます。

---
import Counter from '../components/Counter.tsx';
---
<Counter client:visible />
ディレクティブタイミング
client:loadページロード直後
client:idleブラウザがアイドル状態になったとき
client:visibleコンポーネントが画面に入ったとき
client:mediaメディアクエリにマッチしたとき
client:onlyクライアントレンダリングのみ

必要なものだけJSが配信されるため、ページの初期表示は静的HTMLのままで高速です。

補足: 環境変数の扱い

Astroはビルド時の環境変数を import.meta.env 経由で参照します。

const apiUrl = import.meta.env.PUBLIC_API_URL;

PUBLIC_ プレフィックスが付いたものだけクライアントに露出します。サーバー専用は PUBLIC_ を付けないこと。.env ファイルで定義します。

参考リソース

この技術を体系的に学びたいですか?

未来学では東証プライム上場企業のITエンジニアが24時間サポート。月額24,800円から、退会金0円のオンラインIT塾です。

メールで無料相談する
← 一覧に戻る