はじめに

ブログや成果物を載せていきたいなーと思った時に いろんなサービスを横断的に使うのもいいですけど、 集約するのがいいなと思いましたのでポートフォリオサイトを構築してみることにしました。

ポートフォリオサイトにあるといい機能を洗い出すと、現時点では下記となります。

- 自己紹介 / Profile
- ブログ / Blog
- 成果物情報 / Products
- お問い合わせ / Contact
- プライバシーポリシー等

ブログではメディア媒体を扱いますし、何よりMarkdownなどで書きやすくしておくう必要があります。

今回はAstro + Cloudflareを使い構築していくことにします!

  • Blog / Products
    • [Astro] png,jpegなど画像をastro build実行時に webpに変換できる。
      毎回画像変換なんてやってられません!
    • [Astro] @astrojs/mdxで コンポーネントを埋め込むことができる!
      Markdownファイルで気軽に書くことができ、自分で関数を作り拡張できるのがいい!
  • Contact
    • [Cloudflare] お問合せ受付メールとサクッとメール連携できると楽でいい!

このポートフォリオサイトを構築する過程で学んだTipsをまとめます。

全体構成

src配下はざっくりこのように配置しました。

基本的にAstroベースでシンプル構成です。

src
├── components
│   ├── Blog.astro
│   ├── Contact.astro
│   ├── ContactCTA.astro
│   ├── Footer.astro
│   ├── Nav.astro
│   ├── Products.astro
│   ├── Profile.astro
│   ├── ShareButtons.astro
│   ├── TableOfContents.astro
│   └── mdx
│       ├── AffiliateLink.astro
│       ├── ExternalLink.astro
│       ├── LinkCard.astro
│       └── Video.astro
├── content
│   ├── blog
│   │   ├── 2026-04-13_astro_cloudflare-portfolio
│   │   │   ├── assets1.jpg
│   │   │   ├── cover.png
│   │   │   └── index.mdx
│   │   └── 2026-04-xx_xxxxxxxxx
│   │       ├── assets1.jpg
│   │       ├── cover.png
│   │       └── index.mdx
│   ├── config.ts
│   └── products
│       ├── Product1.mdx
│       └── Product2.mdx
├── layouts
│   └── Layout.astro
└── pages
    ├── api
    │   └── contact.ts
    ├── blog
    │   ├── [slug].astro
    │   └── index.astro
    ├── contact.astro
    ├── index.astro
    ├── privacy-policy.astro
    ├── products
    │   ├── [slug].astro
    │   └── index.astro
    └── rss.xml.ts

ブログ/成果物ページを手軽に更新する仕組みにする

フォルダ構成でアセットを管理する

フォルダごとに記事をまとめると、画像・動画を同じ場所に置けてスッキリします。

src/content/blog/
└── 2026-04-13_astro_cloudflare-portfolio/
    ├── index.mdx   ← 記事本文
    ├── cover.png   ← カバー画像
    └── assets1.jpg ← カバー以外の画像

Content Collectionsを活用して、簡単に一覧ページに反映

Markdownファイルをコレクションとして管理することで、型安全なフロントマターが使えます。

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

const blog = defineCollection({
  type: 'content',
  schema: ({ image }) =>
    z.object({
      title: z.string(),
      date: z.string(),
      tags: z.array(z.string()),
      excerpt: z.string(),
      emoji: z.string(),
      cover: image().optional(),
      draft: z.boolean().default(false),
    }),
});

const products = defineCollection({
  type: 'content',
  schema: z.object({
    name: z.string(),
    description: z.string(),
    emoji: z.string(),
    tags: z.array(z.string()),
    link: z.string().optional(),
    repo: z.string().optional(),
    status: z.enum(['Released', 'Beta', 'WIP']),
    draft: z.boolean().default(false),
    order: z.number().default(0),
  }),
});

export const collections = { blog, products };

これを各mdxの最初に追加するだけで、反映完了です!

---
title: Astro+CloudflareでポートフォリオサイトをゼロからつくるTips
date: '2026-04-13'
tags: ['Astro', 'Cloudflare']
excerpt: コンテンツ駆動型フレームワーク「Astro」を「Cloudflare」で構築して簡易ポートフォリオサイトを公開する!
emoji: 🖌️
cover: './cover.png'
---

静的パスを生成する

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

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

まとめ

Astroは静的サイトの構築に非常に優れたDXを提供しています。Content Collectionsを使えばブログ管理も型安全になります。

ぜひ試してみてください!

2020/11/20 山中湖にて