こんにちは😊
株式会社プロドウガの@YushiYamamotoです!
らくらくサイトの開発・運営を担当しながら、React.js・Next.js専門のフリーランスエンジニアとしても活動しています❗️
普段はNext.js(App Router)でのシステム開発を主戦場にしていますが、最近のWeb制作、特に**「メディアサイト」「コーポレートサイト」の案件においては、Next.jsではなくAstro**を第一選択肢にすることが増えてきました。
「流行っているからAstro」ではなく、「なぜNext.jsではダメで、Astroなのか?」。
今回は、その技術的な判断基準と、実際の移行における思考プロセスを共有します。
1. なぜ今「脱Next.js」が議論されるのか? 🤔
誤解しないでいただきたいのは、私はNext.jsが大好きだということです。
複雑なダッシュボード、認証が絡むSaaS、動的なECサイトにおいて、Next.jsは依然として最強のフレームワークです。
しかし、**「静的なコンテンツが主体のメディアサイト」**において、Next.jsは時として「オーバースペック」であり、パフォーマンスの足かせになることがあります。
🐢 Next.js(SPA/SSR)の課題:Hydrationのコスト
Next.jsは基本的に、サーバーでHTMLを作った後、クライアント側でReactを再起動(Hydration)させて、HTMLを操作可能な状態にします。
これには以下のコストがかかります:
- 巨大なJSバンドルのダウンロード
- ブラウザメインスレッドでのJavaScript実行(解析・評価)
- TBT (Total Blocking Time) の悪化
記事を読むだけのユーザーにとって、ヘッダーやフッター、記事本文をReactで管理する必要は本当にあるのでしょうか?
2. Astroの強力な武器「Islands Architecture」 🏝️
Astroの最大の魅力は、**「デフォルトでJavaScriptをブラウザに送らない(Zero JS)」**という思想です。
必要な部分(例:検索バー、いいねボタン、カルーセル)だけを「島(Island)」として定義し、そこだけReactやVueを起動させます。
Islands Architectureのメリット
ページの90%が静的なメディアサイトでは、JSの転送量を劇的に削減でき、結果としてLighthouseのパフォーマンススコアが容易に90〜100点台に乗ります。
3. Next.js vs Astro 判断基準マトリクス 📊
私がクライアントワークで技術選定をする際、以下の基準で判断しています。
| 比較項目 | Next.js (App Router) 推奨 | Astro 推奨 |
|---|---|---|
| サイト種別 | 管理画面, SNS, SaaS, 動的EC | ブログ, ニュース, LP, コーポレート |
| 更新頻度 | リアルタイム (秒単位) | 定期更新 (CMS連携・ISR相当) |
| ユーザー操作 | 複雑なフォーム, 状態管理 (Redux/Zustand) | 閲覧メイン + 単純なクリック動作 |
| 認証 | 複雑な権限管理が必要 | 基本不要 or 単純なログイン |
| 重視する指標 | UX (遷移の滑らかさ), 開発体験 | SEO, Core Web Vitals, TBT |
🚦 移行の「Go/No-Go」判断フロー
-
「ページ遷移なしで状態を保持する必要があるか?」
- Yes (Spotifyのような音楽プレーヤー、カートの中身) → Next.js
- No (基本はページ遷移でリセットされて良い) → Astro
-
「ログインユーザーごとに全く違う画面を出すか?」
- Yes (マイページ中心) → Next.js
- No (基本は全員同じコンテンツ + 一部パーソナライズ) → Astro
AstroにもView Transitions APIがあり、SPAのような遷移は可能になりましたが、複雑なグローバルステート管理が必要な場合はまだReact/Next.jsのエコシステムの方が堅牢です。
4. 実践:ReactコンポーネントをAstroで動かす 🛠️
「Astroに移行すると、今までのReact資産が無駄になるのでは?」
これが最大の懸念かと思いますが、AstroではReactコンポーネントをそのまま使えます。
以下は、メディアサイトによくある「記事ページ」の実装例です。
記事本文は静的なHTMLとして出力し、「いいねボタン」だけをReactで動かします。
📁 components/LikeButton.tsx (React)
これは普通のReactコンポーネントです。
LikeButton.tsx のコードを見る
import React, { useState } from 'react';
type Props = {
initialCount: number;
};
export const LikeButton = ({ initialCount }: Props) => {
const [count, setCount] = useState(initialCount);
const [isLiked, setIsLiked] = useState(false);
const handleClick = async () => {
if (isLiked) return;
// APIコールのシミュレーション
try {
// await fetch('/api/like', { method: 'POST' });
setCount((prev) => prev + 1);
setIsLiked(true);
console.log('Liked!');
} catch (e) {
console.error(e);
}
};
return (
<button
onClick={handleClick}
disabled={isLiked}
className={`px-4 py-2 rounded-full transition-all ${
isLiked
? 'bg-pink-100 text-pink-600 border-pink-200'
: 'bg-gray-100 hover:bg-gray-200 text-gray-700'
}`}
>
{isLiked ? '❤️ ありがとうございます!' : '🤍 いいね!'}
<span className="ml-2 font-bold">{count}</span>
</button>
);
};
📁 pages/blog/[slug].astro (Astro)
ここでReactコンポーネントを読み込みます。
[slug].astro のコードを見る
---
// サーバーサイドで実行されるフェーズ (ビルド時またはSSR時)
import { getCollection } from 'astro:content';
import Layout from '../../layouts/Layout.astro';
import { LikeButton } from '../../components/LikeButton'; // Reactコンポーネント
// 静的パスの生成 (SSGの場合)
export async function getStaticPaths() {
const posts = await getCollection('blog');
return posts.map((post) => ({
params: { slug: post.slug },
props: { post },
}));
}
const { post } = Astro.props;
const { Content } = await post.render();
// ダミーの初期データ
const initialLikes = 120;
---
<Layout title={post.data.title}>
<article class="max-w-3xl mx-auto py-10">
<h1 class="text-4xl font-bold mb-4">{post.data.title}</h1>
<div class="prose prose-lg mb-10">
{/*
ここは完全に静的なHTMLとして出力されます。
ReactのHydrationコストはゼロです。
*/}
<Content />
</div>
<div class="border-t pt-6">
<p class="mb-4 text-gray-600">この記事が役に立ったら「いいね」をお願いします👇</p>
{/*
ここが重要! client:visible ディレクティブをつけることで、
「ユーザーがこの要素までスクロールした時」に初めて
Reactがロード・起動されます。
*/}
<LikeButton client:visible initialCount={initialLikes} />
</div>
</article>
</Layout>
注意点
Astroコンポーネント内では、Reactの Context や Hooks を親から子へバケツリレーのように渡すことはできません(Islandsが独立しているため)。
コンポーネント間の状態共有には、nanostores などのライブラリを使用するのが一般的です。
5. まとめ:メディアサイトなら迷わずAstroへ 🚀
2025年現在、メディアサイト、コーポレートサイト、ブログにおいて**「Next.js一択」の時代は終わりました**。
- Next.js: アプリケーション開発、動的な機能がメインの場合。
- Astro: コンテンツデリバリーがメイン、パフォーマンス最優先の場合。
「脱Next.js」と言っても、Next.jsを捨てるわけではありません。
**「適材適所」**で使い分けることが、モダンフロントエンドエンジニアに求められるスキルセットだと確信しています。
もし現在、Next.jsで運用しているメディアサイトのパフォーマンス(特にCore Web Vitals)に悩んでいるなら、Astroへの移行、あるいは新規LPでのAstro採用を強くお勧めします!
明日の記事もお楽しみに!👋
最後に:業務委託のご相談を承ります
私は業務委託エンジニアとしてWEB制作やシステム開発を請け負っています。最新技術を活用したレスポンシブなWebサイト制作、インタラクティブなアプリケーション開発、API連携など幅広いご要望に対応可能です。
「課題解決に向けた即戦力が欲しい」「高品質なWeb制作を依頼したい」という方は、お気軽にご相談ください。一緒にビジネスの成長を目指しましょう!
👉 ポートフォリオ
🌳 らくらくサイト