LaravelをAPIサーバー、Next.jsを静的サイトとして使うと、管理しやすいバックエンドと高速なフロントを分けて構築できます。WordPress以外でメディアや小規模サイトを作りたいときの選択肢になります。
全体構成
- Laravel: 記事データをJSONで返すAPI
- Next.js: build時にAPIを取得してHTMLを書き出す
- 公開サーバー: Next.jsの静的ファイルを配信する
- 管理画面が必要ならLaravel側に作る
Laravel側のAPI
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Route;
Route::get('/api/posts', function () {
$posts = [
[
'slug' => 'first-post',
'title' => '最初の記事',
'excerpt' => 'Laravel APIから返した記事です。',
'body' => '<p>本文が入ります。</p>',
],
];
return response()->json($posts);
});Next.jsを静的書き出し設定にする
const nextConfig = {
output: 'export',
images: {
unoptimized: true,
},
};
export default nextConfig;APIから記事を取得する
type Post = {
slug: string;
title: string;
excerpt: string;
body: string;
};
export async function getPosts(): Promise<Post[]> {
const response = await fetch(`${process.env.API_BASE_URL}/api/posts`, {
next: { revalidate: false },
});
if (!response.ok) {
throw new Error('Failed to fetch posts');
}
return response.json();
}一覧ページ
import { getPosts } from '@/lib/posts';
export default async function HomePage() {
const posts = await getPosts();
return (
<main>
<h1>SSG Blog</h1>
<ul>
{posts.map((post) => (
<li key={post.slug}>
<a href={`/posts/${post.slug}`}>{post.title}</a>
<p>{post.excerpt}</p>
</li>
))}
</ul>
</main>
);
}詳細ページ
import { getPosts } from '@/lib/posts';
export async function generateStaticParams() {
const posts = await getPosts();
return posts.map((post) => ({ slug: post.slug }));
}
export default async function PostPage({ params }: { params: { slug: string } }) {
const posts = await getPosts();
const post = posts.find((item) => item.slug === params.slug);
if (!post) {
return <main>Not found</main>;
}
return (
<main>
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.body }} />
</main>
);
}注意点
- SSGなので公開後のデータ更新には再ビルドが必要
- 問い合わせフォームなど動的機能は別APIに逃がす
- 画像最適化は静的書き出し時に制約がある
- API_BASE_URLは環境変数で管理する
- Next.jsの静的書き出しに非対応の機能を使わない
参考資料
まとめ
LaravelとNext.jsのSSG構成は、更新頻度が高すぎないサイトや、表示速度を優先したいメディアに向いています。まずはLaravelでJSONを返し、Next.jsで一覧と詳細を静的生成する最小構成から始めるのが良いです。