Payload CMS(ペイロード シーエムエス)は、Next.jsアプリの中にそのまま組み込めるTypeScript製のヘッドレスCMSです。コレクションをコードで定義すると、管理画面・REST API・GraphQL API・型定義が自動で生成されます。2026年10月13日時点の安定版はv3.90.2(2026年9月23日)で、10月9日(米国時間)には4.0のベータ版が公開されました。本記事では読み方・インストールからコレクション定義、データ取得、アクセス制御の既定値、日本語化、本番運用、4.0での変更点、Strapiとの違いまでを、v3.90.2の検証環境で生成・起動した結果を交えて整理します。CMSの分類から確認したい場合はヘッドレスCMSとは?従来型との違い・料金比較とプレビュー実装を先に読むと理解が早いです。
まとめ:Payload CMSを選ぶ条件と最新バージョンの要点
Payload CMSは、Next.js+TypeScriptで開発するチームが「CMSをアプリのコードの一部として持ちたい」ときに向きます。設定をpayload.config.tsに書くのでGit管理・型安全・独自ロジックの追加がしやすい一方、管理画面のGUIだけでモデルを作りたい非エンジニア主導の運用には向きません。
- ライセンスはMITで、セルフホストなら無料。マネージドのPayload Cloudは新規プロジェクトのデプロイが一時停止中で、公式はVercelとCloudflareのワンクリックデプロイを案内しています。
- 本番で使うのは3.x系(v3.90.2)。4.0は2026年10月9日公開のベータで、Node.js 24.15.0以上・Next.js 16.4.0以上が必須になります。
- 最初に確かめるべき落とし穴はアクセス制御です。REST APIは何も書かなければログイン必須ですが、Local APIは3.xの既定でアクセス制御を適用しません(4.0で既定が逆になる)。
- GUIでモデルを作りたい・Next.js以外のフロントエンドから使うならStrapi、Next.jsと同じリポジトリで型を共有したいならPayloadです。
Payload CMSとは:Next.jsの中で動くTypeScript製ヘッドレスCMS
Payloadは、コンテンツをJSONで配信するヘッドレスCMSであり、TypeScriptで書くアプリケーションフレームワークでもあります。認証・アクセス制御・バージョン管理・下書き・ファイルアップロードを標準で持ち、GitHubのスター数は45,212(2026年10月13日時点)です。ライセンスはMITなので、受託案件や自社サービスに組み込んでもソフトウェアの使用料はかかりません。
Payload 3.0で変わった点:Next.jsの/appへの直接インストール
2024年11月19日に公開された3.0から、Payloadは独立したサーバーではなくNext.jsの/appフォルダの中で動くようになりました。公式の3.0発表によると、依存パッケージは2.0の88個から27個に減り、Astro・SvelteKit・Remixなど他のNode環境からも使えます。管理画面とAPIがフロントエンドと同じプロセス・同じデプロイ単位にまとまるため、CMS用のサーバーを別に立てて保守する手間がなくなりました。
Figma参加後のオープンソース方針とPayload Cloudの現状
2025年6月17日にFigmaがPayloadチームの参加を発表しました。Payload側の告知では、オープンソースのコアは維持・投資を続けるとしたうえで、Payload Cloudについて「新規プロジェクトのデプロイは一時停止中で、既存のCloudプロジェクトは通常どおり稼働する」と説明しています。2026年10月13日時点では、Cloudの新規作成画面に「Enterpriseチームのみ作成できる」と表示されます。つまり、これから始める案件は自前のインフラかVercel・Cloudflareに載せる前提で見積もるのが現実的です。
Payload CMSのインストール:v3.90.2の要件と手順
動作要件:Node.js 20.18.1以上とNext.jsの対応範囲
公式のインストール手順が挙げる要件は、Node.js 20.18.1以上、pnpm・npm・yarn 2以上のいずれか(yarn 1.xは非対応)、MongoDB・Postgres・SQLiteのいずれかです。v3.90.2のタグ時点の文書は「20.9.0以上」でしたが、v3.43.0で入った依存ライブラリのundici@7が20.18.1以上を要求するため、2026年10月5日に3.xの下限が20.18.1へ引き上げられました。Node.js 20自体は2026年4月30日でサポートが終わっているので、新規構築では22か24のLTSを使います。Next.jsは版の範囲が細かく決まっており、v3.90.2の@payloadcms/nextが宣言する対応範囲は「15.2.9〜15.2.x、15.3.9〜15.3.x、15.4.11〜15.4.x、16.3.3以上17未満」です。ドキュメントには「16.2.6以上」と書かれていますが、インストール時の判定はパッケージの宣言で行われるので、既存のNext.jsアプリに入れるときはpackage.jsonのnextの版をこの範囲に合わせてください。Next.js 16系の基本はNext.jsとは?16系App Routerで最小アプリを動かす手順と採用判断で扱っています。
create-payload-appによるプロジェクト作成
新規プロジェクトはCLIで作ります。テンプレートとデータベースをフラグで指定すると、対話なしで最後まで進みます。
npx create-payload-app@latest -n my-app -t blank --db sqlite --db-connection-string "file:./my-app.db" --use-npm --no-agent
cd my-app
npm run dev
4.0の正式版が出ると@latestは4.xを指すので、3.xで始めるならcreate-payload-app@3と版を付けます。v3.90.2で選べるテンプレートはblank・website・ecommerce・with-cloudflare-d1・pluginです。-a claudeなどを付けると、コーディングエージェント用のPayloadスキルも一緒に入ります。既存のNext.jsアプリに入れる場合は、そのフォルダでnpx create-payload-appを実行します。
2026年10月13日にmacOS(Node.js 26.5.0・npm 11.17.0)で同じ条件([email protected]・blank・SQLite)のコマンドを実行すると、生成と依存の導入に2分13秒かかり、node_modulesは875MBになりました。入った版はnext 16.3.3・react 19.2.6・typescript 5.7.3で、npm run devは1.3秒で起動しますが、/adminを初めて開いたときは表示までに約38秒(サーバーログ上は34.6秒)かかります。最初の管理者は/adminの画面かPOST /api/users/first-registerで作ります。
データベースアダプタの選び方:MongoDB・Postgres・SQLite・D1
公式が正式にサポートするアダプタは、Mongoose経由のMongoDB、Drizzle経由のPostgres、Drizzle経由のSQLiteの3系統です。ほかにVercel向けの@payloadcms/db-vercel-postgresと、Cloudflare D1向けの@payloadcms/db-d1-sqliteがあります。
- MongoDB:1件のドキュメントがそのまま1件のデータとして入る。ブロックや配列が多く、形が頻繁に変わるモデル向け。
- Postgres:リレーションの整合性をDB側で担保したい、SQLで集計したい案件向け。DrizzleORMの使い方はDrizzle ORM(ドリズル)とは?が参考になります。
- SQLite:ローカル検証や小規模サイト向け。D1用の
@payloadcms/db-d1-sqliteとファイル保存用のR2を組み合わせると、Cloudflareだけで完結します。
import { buildConfig } from 'payload'
import { postgresAdapter } from '@payloadcms/db-postgres'
import { Posts } from './collections/Posts'
export default buildConfig({
secret: process.env.PAYLOAD_SECRET || '',
db: postgresAdapter({
pool: { connectionString: process.env.DATABASE_URL },
}),
collections: [Posts],
})
コレクション・フィールド・ブロックの定義
Payloadのデータモデルは設定ファイルにコードで書きます。保存すると開発サーバーが管理画面・API・型(payload-types.ts)を作り直すので、CMS側のモデルとフロントエンドの型がずれにくくなります。
コレクションとフィールドの定義
コレクションは投稿やユーザーなどの「入れ物」で、slugがAPIのパスとテーブル名になります。フィールドはtext・richText・relationshipなどの型で指定します。リッチテキストの既定エディタはLexicalです。
import type { CollectionConfig } from 'payload'
export const Posts: CollectionConfig = {
slug: 'posts',
admin: { useAsTitle: 'title' },
access: {
read: () => true, // 公開記事なので未ログインでも読める
},
fields: [
{ name: 'title', type: 'text', required: true },
{ name: 'body', type: 'richText' },
{ name: 'author', type: 'relationship', relationTo: 'users' },
],
}
ブロックフィールドによる可変レイアウト
ランディングページのように「見出し」「CTA」などを編集者が自由に並べ替えたいときはblocks型を使います。フロントエンドは各要素のblockTypeで描画を分けます。
{
name: 'layout',
type: 'blocks',
blocks: [
{ slug: 'hero', fields: [{ name: 'heading', type: 'text' }] },
{ slug: 'cta', fields: [{ name: 'label', type: 'text' }, { name: 'url', type: 'text' }] },
],
}
SQLiteで動かすと、このブロックはブロックの種類ごとにposts_blocks_hero・posts_blocks_ctaという別テーブルに保存されました。PostgresやSQLiteではブロックの種類を増やすたびにテーブルが増えるので、本番では後述の「本番運用」の章で扱うマイグレーションが必要になります。MongoDBなら1件のドキュメントの中に入ります。
Local API・REST・GraphQLによるデータ取得
Payloadは同じデータに3つの経路でアクセスできます。Next.jsと同じプロセスで動くのがPayloadの特徴なので、サーバー側はLocal APIを使い、外部からの取得だけをRESTとGraphQLに任せると無駄な通信が減ります。
| 経路 | 呼び出し | 使う場面 |
|---|---|---|
| Local API | payload.find() | サーバーコンポーネント・スクリプト |
| REST API | /api/posts | 外部サービス・モバイルアプリ |
| GraphQL | /api/graphql | 必要な項目だけを1回で取る |
Next.jsサーバーコンポーネントからのLocal API取得
Local APIはHTTPを通らずにデータベースへ直接アクセスするので、ネットワークの往復もCORSの設定も要りません。下のページはv3.90.2の検証環境(2026年10月13日)で/postsを開き、投稿のタイトルが一覧表示されることと、tsc --noEmitが型エラーなしで通ることを確認しました。
import { getPayload } from 'payload'
import config from '@payload-config'
export default async function PostsPage() {
const payload = await getPayload({ config })
const { docs } = await payload.find({
collection: 'posts',
depth: 0,
limit: 10,
overrideAccess: false, // 公開ページでは未ログインとして権限を確かめる
})
return (
<ul>
{docs.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
REST・GraphQLによる外部からの取得
RESTは/api/<slug>、GraphQLは/api/graphqlに自動で作られます。認証付きで呼ぶときはAuthorization: JWT <token>ヘッダーを付けます。
curl "http://localhost:3000/api/posts?limit=10&depth=0"
curl -X POST http://localhost:3000/api/graphql \
-H "Content-Type: application/json" \
-d '{"query":"{ Posts(limit: 5) { docs { title layout { __typename } } totalDocs } }"}'
注意したいのがdepthです。3.xの既定は2で、指定しないとリレーション先を2階層まで展開します。一覧ページで著者の詳細まで取ってしまうと応答が重くなるので、一覧はdepth=0、詳細ページだけ必要な深さを指定するのが基本です。
アクセス制御の既定値:RESTは403、Local APIは全件返る
Payloadで事故につながりやすいのが、経路によってアクセス制御の既定値が逆になる点です。公式ドキュメントは、既定のアクセス制御を「リクエストにユーザーがいるかだけを確かめる関数」とし、Local APIでは既定でアクセス制御が飛ばされると書いています。v3.90.2の検証環境で、accessを書かないnotesコレクションを作って確かめた結果が次の表です。
| 呼び出し方 | 結果 |
|---|---|
| REST・未ログインでGET | 403 |
| REST・ログイン済みでGET | 200(全件) |
| Local API・既定 | 全件返る |
| Local API・overrideAccess: false | Forbiddenエラー |
このため、公開ページからpayload.find()を呼ぶときにoverrideAccess: falseを付け忘れると、下書きや会員限定のデータまでページに出てしまいます。公開ページのコードには必ずoverrideAccess: falseを書き(下書きを隠すには、readで_statusがpublishedのものだけを返す条件も必要)、ログインユーザーとして読むときはuserも渡してください。
もう1つ、usersコレクションにaccessを書かないと「ログイン済みなら誰でも読める」状態になります。ログイン済みで/api/postsを既定の深さで取ると、著者として紐づいたユーザーのメールアドレスとsessions(ログインセッションの一覧)まで応答に含まれました(未ログインでは著者はIDだけ)。管理者だけが使うCMSなら問題になりにくいものの、会員登録を開放するサイトではusersのreadを「本人と管理者だけ」に絞る必要があります。
管理画面の日本語化とコンテンツの多言語化
管理画面の表示言語はi18n設定で決めます。@payloadcms/translations(npm i @payloadcms/translationsで追加)には日本語(ja)が入っており、supportedLanguagesに登録すると日本語を選べるようになります。表示言語はリクエスト(ブラウザの言語設定)から自動で判定され、判定できないときはfallbackLanguageの言語になります。検証環境(fallbackLanguage: 'ja')では、Accept-Languageを付けないAPIリクエストのエラーメッセージが「このアクションは許可されていません。」と日本語で返り、Accept-Language: enを付けると英語で返りました。各ユーザーはアカウント画面で表示言語を切り替えられます。
import { en } from '@payloadcms/translations/languages/en'
import { ja } from '@payloadcms/translations/languages/ja'
export default buildConfig({
i18n: {
supportedLanguages: { en, ja },
fallbackLanguage: 'ja',
},
// 記事本文を多言語で持つ場合は localization(フィールド単位)
localization: {
locales: ['ja', 'en'],
defaultLocale: 'ja',
},
// ...
})
i18nは管理画面のUIの言語で、記事本文の多言語化とは別物です。本文を日英で持つ場合はlocalizationを設定し、訳したいフィールドにlocalized: trueを付けます。Payloadの多言語化はドキュメント単位ではなくフィールド単位なので、画像やスラッグを言語間で共有したまま、タイトルと本文だけを訳し分けられます。
本番運用:マイグレーション・ファイル保存・デプロイ先
開発のpushモードと本番のmigrate
PostgresとSQLiteでは、開発サーバーの起動中はDrizzleのpushモードがスキーマの変更をそのままローカルDBへ反映します。本番DBにはマイグレーションで反映するのが公式の推奨手順で、同じDBでpushとマイグレーションを混ぜないよう警告されています。
npm run payload migrate:create add-layout-blocks # 変更をマイグレーションファイルにする
npm run payload migrate:status # 未実行のマイグレーションを確認
npm run payload migrate # CIやデプロイ前に本番DBへ適用
アップロードファイルの保存先とストレージアダプタ
Payloadの既定ではアップロードファイルをサーバーのディスクに保存します。Vercelのようなサーバーレス環境や、再起動でディスクが消えるホスティングではファイルが消えるので、ストレージアダプタで外部に置きます。公式アダプタはVercel Blob・AWS S3・Azure・Google Cloud Storage・Uploadthing・Cloudflare R2の6種類です(Uploadthingは4.0 betaで削除済み)。
デプロイ先と料金(セルフホスト・Vercel・Cloudflare)
PayloadはNext.jsが動く場所ならどこにでもデプロイでき、公式のデプロイ手順はVercel・Cloudflare Workers・Dockerの例を載せています。本体の料金はかからず、ユーザー数やコンテンツ件数による課金もありません。SSOや公開ワークフロー、ビジュアルエディタなどはEnterprise向けの有料機能(要問い合わせ)です。かかるのはホスティング・DB・ストレージの費用と、モデル設計や移行の開発工数です。本番ではPAYLOAD_SECRETを推測できない長い文字列にし、環境変数で渡してください。
Payload 4.0 betaの変更点と移行前の確認事項
2026年10月9日にv4.0.0-beta.0が公開されました。管理画面の再デザイン、TanStack Start対応、ダッシュボード、階層・タグ、グリッド表示、アップロードファイルのバージョン管理、MCPの作り直しが主な新機能で、npx create-payload-app@betaで試せます。要件は次のように上がります。
| 項目 | v3.90.2 | 4.0 beta |
|---|---|---|
| Node.js | 20.18.1以上 | 24.15.0以上 |
| Next.js | 15.2.9〜15.4.x(一部)・16.3.3〜16.x | 16.4.0以上 |
| TypeScript | 5.7.3以上 | 6.0.3以上 |
| Local APIのoverrideAccess既定 | true | false |
| depthの既定 | 2 | 1 |
| versions | 指定したときだけ | コレクション・グローバルで既定有効(認証は除く) |
3.0から4.0への移行ガイドで、アプリの挙動が変わるのは主に次の5点です。
- Local APIがアクセス制御を既定で適用する。前章の「既定で全件返る」前提で書いたスクリプトやジョブは、権限エラーで止まるか結果が減る。
depthを省略したときの展開が1階層になる。2階層目のデータを画面で使っていると表示が欠ける。- versionsが既定で有効になる。Postgres・SQLiteの既存DBではバージョン用テーブル(
_<slug>_versions)を追加するマイグレーションが要り、不要ならversions: falseを明示する。 - Slateエディタが削除される。Slateを使っているなら4.0に上げず、3.xの最新版のままLexicalへの移行を先に済ませる。
- JWTのヘッダーに
authVersion: 1が必須になる。修正済みの3.x(検証環境のv3.90.2が発行したトークンには付いていた)から上げれば既存のログインはそのまま使えるが、未修正の版から直接上げると全ユーザーの再ログインと、外部連携が保存しているトークンの再発行が必要になる(移行直後は401が増える)。
破壊的変更の多くはnpx @payloadcms/codemod@betaで自動修正でき、codemodは何度実行しても同じ結果になる作りです。ベータ版の告知自体が「詳細は翌週に出す」としている段階なので、本番は3.xのまま運用し、Node.js 24・Next.js 16.4・TypeScript 6.0.3への更新だけ先に済ませておくのが安全です。
Payload CMSとStrapiの違いと選び分けの条件
同じオープンソースのヘッドレスCMSでも、Payloadは「コードでバックエンドを組むフレームワーク」、Strapiは「管理画面からモデルを作れるプラットフォーム」という違いがあります。
| 観点 | Payload CMS | Strapi |
|---|---|---|
| モデルの作り方 | TypeScriptで定義 | 管理画面のGUIでも作れる |
| Next.jsとの関係 | 同じアプリの中で動く | 別サーバーとしてAPI連携 |
| サーバー側の取得 | Local API(HTTP不要) | REST・GraphQL(プラグイン) |
| マネージド版 | 新規デプロイ停止中 | Strapi Cloudあり |
Next.jsとTypeScriptで作るサイトで、型を共有しながらCMSを同じリポジトリで持ちたいならPayloadを選びます。逆に、Next.js以外のフロントエンドが中心のチーム、モデルを非エンジニアがGUIで足していく運用、マネージドのホスティングを前提にしたい案件では、Payloadを選ぶ理由が薄くなります。Strapiの詳細はStrapiとは?読み方・使い方と料金を最新版5.55で解説、v5の変更点はStrapi v5の変更点とv4からの移行手順にまとめています。SaaS型と比べたい場合はSanity CMSとは?やContentfulとは?API構成と料金の増え方・移行手順を実コードで解説、DB直結型ならDirectusとは?も候補です。既存のWordPressサイトからの移行も含めて構成から相談したい場合は、WordPressサイト制作・CMS構築のページをご覧ください。
よくある質問
Payload CMSの読み方は?
「ペイロード(シーエムエス)」と読みます。payloadは「積み荷・データ本体」を意味する英単語です。
Payload CMSは無料で使えますか?
本体はMITライセンスで、商用でも無料です。費用はホスティング・データベース・ストレージなどのインフラ代と開発工数で、ユーザー数やコンテンツ件数による課金はありません。SSOや公開ワークフローなどのEnterprise機能だけは有料です。
Payload Cloudは今も使えますか?
既存のCloudプロジェクトは稼働を続けていますが、新規プロジェクトのデプロイは一時停止中で、2026年10月時点で新規作成できるのはEnterpriseチームだけです。新しく始めるならセルフホストか、Vercel・Cloudflareのワンクリックデプロイを使います。
Payload CMSにNext.jsは必須ですか?
3.xでは管理画面がNext.jsの中で動くため、管理画面を使うならNext.jsが必要です。データの取得だけなら、3.0の時点でAstro・SvelteKit・Remixなど他のNode環境からも使えます。4.0 betaでは管理画面のルーティングがNext.jsに依存しない作りになり、TanStack Startにも対応しました。
Payload 4.0にはすぐ上げるべきですか?
2026年10月時点ではベータ版なので、本番は3.x(v3.90.2)のままにします。上げる前にNode.js 24.15.0以上・Next.js 16.4.0以上・TypeScript 6.0.3以上へ更新し、Slateエディタを使っていればLexicalへ移行しておきます。