Communitygithub.com

0xDarkMatter/claude-mods

Astro フレームワークのパターン、アイランドアーキテクチャ、コンテンツコレクション、レンダリング戦略、デプロイに関するスキルです。対象: astro、islands architecture、content collections、astro cloudflare、view transitions、partial hydration、astrojs、SSG、SSR、hybrid rendering、astro adapter。

claude-mods とは?

このスキルは、Astro フレームワークを使用した開発のための包括的な知識ベースです。Astro の中核となる概念であるアイランドアーキテクチャ、コンテンツコレクション、ビュートランジション、レンダリング戦略(SSG、SSR、ハイブリッド)、そして Cloudflare、Vercel、Netlify、Node.js などのプラットフォームへのデプロイについて深くカバーしています。開発者は、プロジェクトの要件に応じた最適なレンダリング戦略を決定フローチャートを用いて選択でき、コンテンツコレクションのスキーマ定義やデータ取得方法、アイランドコンポーネントのハイドレーション戦略、ビュートランジションの設定など、実践的なコード例とともに詳細なパターンを学ぶことができます。また、プロジェクト構造のリファレンスや一般的な落とし穴、プロダクションセキュリティのチェックリストも含まれており、Astro プロジェクトの設計からデプロイ、運用までを一貫してサポートします。

対応✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/0xDarkMatter/claude-mods/tree/HEAD/skills/astro-ops

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

Astro Operations

最終確認日: 2026-07

Astro フレームワーク開発のための包括的なパターン集: アイランドアーキテクチャ、コンテンツコレクション、レンダリング戦略、ビュートランジション、マルチプラットフォームデプロイ。

レンダリング戦略 決定木

どのレンダリング戦略を選ぶべきか?
│
├─ コンテンツは主に静的か(ブログ、ドキュメント、マーケティングサイト)?
│  ├─ YES → 更新頻度は1日に1回未満か?
│  │  ├─ YES → SSG (output: 'static')
│  │  │        最速のTTFB、CDNキャッシュ可、ランタイムコストゼロ
│  │  └─ NO  → ハイブリッド (output: 'hybrid')
│  │           デフォルトは静的 + ルートごとにSSRを選択
│  └─ NO  → すべてのページでパーソナライズが必要か?
│     ├─ YES → SSR (output: 'server')
│     │        リクエストごとに動的、認証対応、リアルタイムデータ
│     └─ NO  → ハイブリッド (output: 'hybrid')
│              静的なシェル + 動的な部分はサーバーアイランド
│
├─ アプリにリアルタイムのインタラクティビティが必要か(ダッシュボード、SPA)?
│  ├─ YES → クライアントサイドルーティングを持つ完全なSPAか?
│  │  ├─ YES → React/Vue SPA、または Astro + client:only を検討
│  │  └─ NO  → ハイブリッド + アイランドアーキテクチャ
│  │          静的ページ内のインタラクティブなアイランド
│  └─ NO  → SSG (output: 'static')
│
├─ ビルド時間が懸念されるか(1万ページ以上)?
│  ├─ YES → オンデマンドレンダリングを使用したハイブリッド
│  │        人気のページはプリレンダリング、ロングテールはSSR
│  └─ NO  → SSGで十分
│
└─ エッジコンピューティングが必要か(グローバルな低レイテンシー)?
   ├─ YES → SSR + Cloudflare/Vercel Edge アダプター
   └─ NO  → SSR + Node アダプター、または SSG

設定

// astro.config.mjs
import { defineConfig } from 'astro/config';

// SSG (デフォルト) - すべてのページをビルド時にプリレンダリング
export default defineConfig({
  output: 'static',
});

// SSR - すべてのページをリクエスト時にレンダリング
export default defineConfig({
  output: 'server',
  adapter: cloudflare(), // または vercel(), netlify(), node()
});

// ハイブリッド - デフォルトは静的、ページごとにSSRを選択
export default defineConfig({
  output: 'hybrid',
  adapter: cloudflare(),
});
---
// ハイブリッドモードでは、特定のページでプリレンダリングを無効にする:
export const prerender = false;
// SSRモードでは、プリレンダリングを有効にする:
export const prerender = true;
---

アイランドアーキテクチャ クイックリファレンス

ディレクティブハイドレーションのタイミング送信されるJS使用例
client:loadページ読み込み直後完全バンドルファーストビューのインタラクティブ要素 (ナビ、ヒーローCTA)
client:idleページがアイドル状態になった後 (requestIdleCallback)完全バンドルファーストビュー以下のインタラクティブ要素 (コメントフォーム、チャット)
client:visibleビューポートにスクロールされた時完全バンドルページ下部の要素 (フッターウィジェット、カルーセル)
client:mediaメディアクエリが一致した時完全バンドルモバイル専用ナビ、レスポンシブコンポーネント
client:only="react"即時、SSRを完全にスキップ完全バンドルSSRできないコンポーネント (canvas、WebGL)
(なし)なし - 静的HTMLのみJSゼロ静的コンテンツ、カード、ヘッダー
---
import NavBar from '../components/NavBar.tsx';
import CommentForm from '../components/CommentForm.tsx';
import ImageCarousel from '../components/ImageCarousel.svelte';
import MobileMenu from '../components/MobileMenu.vue';
import ThreeScene from '../components/ThreeScene.tsx';
---

<!-- すぐに読み込む - 重要なインタラクティブ要素 -->
<NavBar client:load />

<!-- ページがアイドル状態になったら読み込む - 重要ではない -->
<CommentForm client:idle />

<!-- ビューポートにスクロールされたら読み込む - 遅延読み込み -->
<ImageCarousel client:visible />

<!-- モバイルでのみ読み込む -->
<MobileMenu client:media="(max-width: 768px)" />

<!-- クライアントのみ、SSRなし (WebGLはサーバーで実行不可) -->
<ThreeScene client:only="react" />

コンテンツコレクション クイックスタート

スキーマを定義する

// src/content.config.ts (Astro 5) または src/content/config.ts (Astro 4)
import { defineCollection, z, reference } from 'astro:content';
import { glob } from 'astro/loaders';

const blog = defineCollection({
  loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/blog' }),
  schema: z.object({
    title: z.string(),
    description: z.string().max(160),
    pubDate: z.coerce.date(),
    updatedDate: z.coerce.date().optional(),
    heroImage: z.string().optional(),
    tags: z.array(z.string()).default([]),
    draft: z.boolean().default(false),
    author: reference('authors'), // 別のコレクションを参照
  }),
});

const authors = defineCollection({
  loader: glob({ pattern: '**/*.json', base: './src/content/authors' }),
  schema: z.object({
    name: z.string(),
    avatar: z.string(),
    bio: z.string(),
    socials: z.object({
      twitter: z.string().optional(),
      github: z.string().optional(),
    }).optional(),
  }),
});

export const collections = { blog, authors };

コレクションをクエリする

---
import { getCollection, getEntry } from 'astro:content';

// 下書きではないすべてのブログ投稿を日付順に取得
const posts = (await getCollection('blog', ({ data }) => !data.draft))
  .sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());

// 1つのエントリを取得
const post = await getEntry('blog', 'my-first-post');

// 参照を解決
const author = await getEntry(post.data.author);

// コンテンツをレンダリング
const { Content, headings } = await post.render();
---

<Content />

コンテンツコレクション vs 外部CMS

基準コンテンツコレクション外部CMS (Payload など)
コンテンツタイプローカルのMarkdown/MDX、ドキュメント、ブログリレーショナルデータモデル
作成者開発者 (バージョン管理による)編集者 (管理UI、マルチユーザー認証)
バリデーションビルド時のZodによる型安全CMS側のスキーマ + API契約
更新頻度サイトと一緒にデプロイデプロイとは独立
API要件なし (ビルド時のクエリ)他のコンシューマ向けREST/GraphQL
ワークフローGit PR、シンプルなレビュー編集ワークフロー、下書き、ロール

経験則: 最初はコンテンツコレクションから始め、デプロイなしで公開する必要がある非開発者、またはコンテンツが本格的に関連している場合のみCMSの利用を検討する。

プロジェクト構造 リファレンス

project-root/
├── astro.config.mjs          # Astro 設定
├── tsconfig.json              # TypeScript 設定 (astro/tsconfigs を継承)
├── package.json
├── public/                    # 静的アセット (そのままコピー)
│   ├── favicon.svg
│   ├── robots.txt
│   └── og-image.png
├── src/
│   ├── pages/                 # ファイルベースルーティング
│   │   ├── index.astro        # → /
│   │   ├── about.astro        # → /about
│   │   ├── blog/
│   │   │   ├── index.astro    # → /blog
│   │   │   └── [slug].astro   # → /blog/:slug (動的)
│   │   ├── api/
│   │   │   └── search.ts      # → /api/search (APIエンドポイント)
│   │   └── [...slug].astro    # → キャッチオール/404
│   ├── layouts/
│   │   ├── BaseLayout.astro   # HTMLシェル、ヘッド、グローバルスタイル
│   │   └── BlogPost.astro     # ブログ投稿レイアウト
│   ├── components/
│   │   ├── Header.astro       # 静的Astroコンポーネント
│   │   ├── Footer.astro
│   │   ├── NavBar.tsx         # Reactアイランド
│   │   └── Counter.svelte     # Svelteアイランド
│   ├── content/               # コンテンツコレクションのソースファイル
│   │   ├── blog/
│   │   │   ├── post-one.md
│   │   │   └── post-two.mdx
│   │   └── authors/
│   │       └── jane.json
│   ├── content.config.ts      # コレクションスキーマ (Astro 5)
│   ├── middleware.ts           # リクエスト/レスポンスミドルウェア
│   ├── styles/
│   │   └── global.css
│   └── lib/                   # 共有ユーティリティ
│       ├── utils.ts
│       └── constants.ts
└── .env                       # 環境変数

ビュートランジション クイックリファレンス

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

<html>
  <head>
    <ViewTransitions />
  </head>
  <body>
    <slot />
  </body>
</html>

トランジションディレクティブ

<!-- ページ間で要素を維持 (状態を保持、再レンダリングを回避) -->
<audio transition:persist id="player">
  <source src="/music.mp3" />
</audio>

<!-- アニメーションペアリングのための名前付きトランジション -->
<img transition:name="hero" src={post.heroImage} />

<!-- カスタムアニメーション -->
<div transition:animate="slide">コンテンツ</div>
<div transition:animate="fade">コンテンツ</div>
<div transition:animate="none">アニメーションなし</div>

<!-- 名前を指定して維持 (複数の永続要素の場合) -->
<video transition:persist="media-player" />

ライフサイクルイベント

<script>
  document.addEventListener('astro:before-preparation', (e) => {
    // 新しいページが取得される前 - ナビゲーションをキャンセル、ローディング表示
  });

  document.addEventListener('astro:after-preparation', (e) => {
    // 新しいページが取得された後、交換前
  });

  document.addEventListener('astro:before-swap', (e) => {
    // DOM交換動作をカスタマイズ
  });

  document.addEventListener('astro:after-swap', () => {
    // DOM更新完了 - スクリプトを再初期化
  });

  document.addEventListener('astro:page-load', () => {
    // ページが完全に読み込まれた (初回 + すべてのナビゲーションで発火)
    // View Transitionsでは、DOMContentLoadedの代わりにこれを使用する
  });
</script>

戻る/進む処理

// astro.config.mjs
export default defineConfig({
  prefetch: {
    prefetchAll: true,         // ホバー時にすべてのリンクをプリフェッチ
    defaultStrategy: 'hover',  // 'hover' | 'tap' | 'viewport' | 'load'
  },
});
<!-- リンクごとのプリフェッチ制御 -->
<a href="/about" data-astro-prefetch>ホバーでプリフェッチ (デフォルト)</a>
<a href="/blog" data-astro-prefetch="viewport">表示されたらプリフェッチ</a>
<a href="/contact" data-astro-prefetch="load">すぐにプリフェッチ</a>
<a href="/external" data-astro-prefetch="false">プリフェッチしない</a>

デプロイ決定木

どこにデプロイするか?
│
├─ エッジコンピューティング + Cloudflareエコシステム (KV、D1、R2) が必要?
│  └─ Cloudflare Pages/Workers
│     アダプター: @astrojs/cloudflare
│     最適: グローバルエッジ、Workersバインディング、コスト効率
│
├─ サーバーレス + Vercelエコシステム (ISR、アナリティクス) が必要?
│  └─ Vercel
│     アダプター: @astrojs/vercel
│     最適: Next.jsからの移行、画像最適化、ISR
│
├─ サーバーレス + Netlifyエコシステム (フォーム、認証) が必要?
│  └─ Netlify
│     アダプター: @astrojs/netlify
│     最適: JAMstack、組み込みフォーム、分割テスト
│
├─ 完全なサーバー制御 (Docker、カスタムランタイム) が必要?
│  └─ Node.js (スタンドアロン、またはExpress/Fastify)
│     アダプター: @astrojs/node
│     最適: セルフホスト、WebSocket、長時間実行プロセス
│
└─ 純粋な静的サイト (SSR不要)?
   └─ 任意の静的ホスト (GitHub Pages、S3、Cloudflare Pages)
      アダプター不要、output: 'static'
      最適: ブログ、ドキュメント、マーケティングサイト

アダプターのインストール

# Cloudflare
npx astro add cloudflare

# Vercel
npx astro add vercel

# Netlify
npx astro add netlify

# Node.js
npx astro add node

よくある落とし穴

落とし穴原因修正方法
ハイドレーションの不一致エラーサーバーHTMLとクライアントレンダリングの差異 (日付、ランダムID、ブラウザAPI)ブラウザ依存のコンポーネントには client:only を使用するか、決定論的なレンダリングを保証する
クライアントで import.meta.env が未定義PUBLIC_ プレフィックス付きの変数のみがクライアントサイドコードに公開されるPUBLIC_MY_VAR に名前を変更するか、サーバーからプロップ経由で渡す
SSGで動的ルートが404になるgetStaticPaths() がすべての可能なパラメータを返していないgetStaticPaths() がすべての有効なパスを返すことを確認するか、ハイブリッド/SSRに切り替える
画像が最適化されないAstroの <Image /> コンポーネントの代わりに <img> を使用しているastro:assets からインポート: import { Image } from 'astro:assets'、srcにはローカルインポートを使用する
アダプターなしでSSRが失敗するoutput: 'server' または 'hybrid' にはデプロイアダプターが必要アダプターをインストール: npx astro add cloudflare (または vercel, netlify, node)
MDXコンポーネントがレンダリングされないカスタムコンポーネントがMDXコンテンツに渡されていない<Content components={{ MyComponent }} /> でコンポーネントを渡すか、astro.config.mjs のMDX設定を使用する
コンテンツコレクションのスキーマ変更が反映されない型生成がキャッシュされ、古い .astro 型が残っているastro sync を実行して型を再生成し、開発サーバーを再起動する
Astroコンポーネントでの client:*クライアントディレクティブはフレームワークコンポーネント (React、Vue、Svelte) でのみ機能するAstroコンポーネントは静的にのみ使用可能。インタラクティブな部分をフレームワークコンポーネントに抽出する
document / window が定義されていないサーバーサイドコードはブラウザのグローバル変数にアクセスできないif (typeof window !== 'undefined') でガードするか、client:only に移動する
コンポーネント間でスタイルが漏れるスコープ付きスタイルの代わりにグローバルCSSを使用している<style> (.astroではデフォルトでスコープ付き) または意図的に <style is:global> を使用する
View Transitionsがスクリプトを壊すDOMContentLoaded はView Transitionsで一度しか発火しない代わりに astro:page-load イベントを使用する。これはすべてのナビゲーションで発火する
環境変数が本番環境で欠落する.env が読み込まれていない、またはプラットフォームの環境変数が設定されていないastro.config.mjs で envField を使用してバリデーションする。プラットフォームのダッシュボードで変数を設定する

プロダクションセキュリティチェックリスト

すべてのプロダクションデプロイでは、以下に対処すること:

  • CSPヘッダー - 制限的な Content-Security-Policy を設定する (references/deployment.md のミドルウェアパターンを参照)
  • リモート画像の制限 - 明示的な image.domains / remotePatterns 許可リストを適用する。ユーザー入力から画像URLを導出しないこと (SSRFリスク)
  • ホストヘッダーの検証 - リクエストホストが期待されるドメインと一致することをミドルウェアで検証する (SSR/ハイブリッドのみ)
  • シークレット管理 - Cloudflareでは、コードに焼き付ける環境変数ではなく、Workers バインディング (wrangler secret put) を使用する。その他のプラットフォームでは、プラットフォームのシークレットストアを使用する
  • HTTPSのみ - すべての外部リソース (スクリプト、画像、フォント) がHTTPS経由で読み込まれることを確認する
  • 入力バリデーション - SSRコンテキスト内のすべてのユーザー入力をサニタイズする (クエリパラメータ、フォーム本文、Cookie)

リファレンスファイル

ファイル内容行数
references/content-collections.mdスキーマパターン、Zod型、クエリ、MDX、コンテンツレイヤーAPI、移行~500
references/islands-rendering.mdハイドレーション戦略、パフォーマンス、フレームワーク間の考慮事項~400
references/view-transitions.mdアニメーション、ライフサイクル、アクセシビリティ、アダプティブな動作~350
references/deployment.mdアダプター設定、環境変数、ミドルウェア、CSP、CI/CD~450
references/dev-tools.mdAstro Dev Toolbar、インテグレーション、プラグイン、デバッグ~200
references/astro-cheatsheet.mdクイックリファレンス (CLIコマンド、ディレクティブ、設定、テンプレート構文)~150

さらに詳しく

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

関連スキル