Claude CodeでGutenbergブロックを作る — create-blockとwp-envで検証
Claude Codeで@wordpress/create-blockの雛形を育て、wp-envの実機で表示を確かめる手順です。block.jsonの編集と、動的・静的レンダリングの検証点を扱います。
Gutenbergブロックは、block.json、edit.js、render.phpといった決まった役割のファイル群で成り立っています。役割が決まっているので、Claude Codeに任せる範囲を切りやすい開発対象です。ただし画面の見た目はコードを読むだけでは判断できません。そこで@wordpress/create-blockで雛形を作り、wp-envの実機でブロックを確かめる流れをClaude Codeに回させます。
プラグインのヘッダーやフック、名前衝突の扱いはClaude CodeでWordPressプラグイン開発を始めるにあります。ここでは、その上に載るブロック側の作業を扱います。
Gutenbergブロックとは何か、Claude Codeに何を渡すか
Gutenbergブロックは、WordPressのブロックエディターに挿入できる部品です。カスタムブロックは通常プラグインとして登録し、ブロックの定義は決まったファイル構成で書きます。テーマにも埋め込めますが、公式はテーマを切り替えてもブロックが残るように、プラグインに置く方式を勧めています。
create-blockが生成するファイルの役割は次のとおりです。Claude Codeへの指示は、この単位で切ると伝わりやすくなります。
| ファイル | 役割 |
|---|---|
block.json | 役割ブロックの名前・属性・supports・各ファイルの場所 |
src/index.js | 役割クライアント側でブロックを登録するエントリ |
src/edit.js | 役割エディター内のUI(Reactコンポーネント) |
src/save.js | 役割DBに保存する静的HTMLを返す関数 |
src/render.php | 役割フロントエンドの出力をサーバーで組み立てる |
build/ | 役割src/をビルドした成果物。WordPressが実際に読む |
save.jsは、動的ブロックとして雛形を作ると最初は存在しません。静的な保存が必要になったときに足します。
Claude Codeに渡す前提は3つです。ブロックの識別名はblock.jsonのnameが正であること。エディターの見た目はsrc/を直しただけでは変わらず、ビルド後のbuild/が使われること。そして、画面で確認するまでは完成と言えないことです。
create-blockで雛形を作る
Node.jsとnpmが入っていれば、雛形は1コマンドで作れます。公式チュートリアルは、著作権表示ブロック(Copyright Date Block)をこの形で始めています。
npx @wordpress/create-block@latest copyright-date-block --variant=dynamic
cd copyright-date-block指定したスラッグ(copyright-date-block)が、フォルダ名とブロックの内部名になります。--variant=dynamicを付けると、サーバーで描画する動的ブロックの雛形になります。
対話形式で選びたいときは、引数なしでnpx @wordpress/create-block@latestを実行します。名前空間まで指定するなら、次の形です。
npx @wordpress/create-block@latest --namespace="my-plugin" --slug="my-block" --variant="dynamic"雛形にはwp-scriptsが組み込まれ、package.jsonに次のスクリプトが入ります。
| スクリプト | 用途 |
|---|---|
npm run start | 用途変更を監視し、保存のたびに再ビルドする |
npm run build | 用途本番向けに最適化する |
npm run lint:js | 用途JavaScriptのlint |
npm run lint:css | 用途CSSのlint |
npm run format | 用途コード整形 |
npm run plugin-zip | 用途配布用のzipを作る |
Claude Codeに検証させるコマンドは、この中から選びます。buildが通ること、lint:jsが通ることが、最低限の機械的な合格ラインです。
wp-envでブロックを実機検証する環境を作る
ビルドが通っても、ブロックがエディターに出るかは別問題です。wp-envは、DockerでローカルのWordPressを立てる公式ツールで、プラグインやテーマを設定なしでマウントして有効化できます。
必要なものはDocker Desktopとwp-env本体です。Docker Desktopは小規模事業者・個人利用・教育・非営利のオープンソースでは無料と案内されています。
npm -g install @wordpress/env
wp-env --version
cd copyright-date-block
wp-env start起動後はhttp://localhost:8888でサイトが開きます。管理画面のログインは、ユーザー名admin、パスワードpasswordです。これは検証用の既定値なので、ローカル環境の中だけで使います。
生成したプラグインのフォルダでwp-env startを実行すると、そのプラグインが自動でマウントされ有効化されます。プラグインでもテーマでもないフォルダで実行すると、素のWordPress環境ができ、.wp-env.jsonが見つからないという警告が出ます。意図どおりなら無視して構いません。
複数のプラグインやテーマ、ポート、PHPバージョンを指定したいときは、実行するフォルダに.wp-env.jsonを置きます。
{
"$schema": "https://schemas.wp.org/trunk/wp-env.json",
"plugins": [ "." ],
"port": 8888
}pluginsに"."を指定すると、そのフォルダ自体が有効化されます。portの既定値は8888、テスト用サイトのtestsPortは8889です。他のサーバーと衝突するなら、WP_ENV_PORT=3333 wp-env startのように環境変数で変えられます。環境変数は設定ファイルより優先されます。
作り直したいときのコマンドも押さえておきます。
wp-env clean all: データベースを初期化するwp-env destroy: そのプロジェクトの環境を丸ごと削除するwp-env stop: 起動中のコンテナを止める
CLAUDE.mdに検証ループを固定する
環境ができたら、検証の手順をCLAUDE.mdに書きます。毎回口頭で伝えるより、プロジェクトに置いたほうが再現性があります。/initで雛形を作り、そこへブロック開発の約束を足す形が手軽です。
# WordPressブロック開発の約束
## コマンド
- ビルド: `npm run build`
- lint: `npm run lint:js`
- 実機確認: `wp-env start`(http://localhost:8888、admin / password)
## 守ること
- ブロックの名前・属性・supportsは`src/block.json`を正とし、JSとPHPで同じ属性名を使う
- `supports`で足りる機能(色・文字サイズなど)は、独自実装より先に`supports`を使う
- `src/`を直したら、`npm run build`が通ってから完了を報告する
- 表示の確認は`wp-env`の実機で行い、確認した内容をそのまま報告する
- `build/`を手で編集しない「supportsを先に使う」は、公式チュートリアルの方針に沿った1行です。公式は、ネイティブのブロックサポートを使うと、利用者にも一貫した編集体験が届き、少ないコードでコアの機能を受けられると説明しています。
CLAUDE.mdは200行未満が目安とされています。ブロック開発の約束は、10行前後に収まる量にとどめます。
編集のたびにlintを走らせるhook
CLAUDE.mdの約束は、守られないこともあります。機械的に強制したい検査はhookに寄せます。PostToolUseイベントにEdit|Writeのmatcherを付けると、ファイルを編集するツールの直後だけ実行されます。.claude/settings.jsonに次の設定を足す形です。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "npm run lint:js"
}
]
}
]
}
}公式ガイドの例は、編集したファイルのパスをjqで取り出してPrettierに渡す形です。ここではlint:jsをプロジェクト全体に走らせる簡略形にしました。render.phpを直したときにもlintが走るため、無駄が気になるなら、パスを見て.jsだけに絞る書き方に変えます。
hookが成功したときは、会話には何も表示されません。動いたか確かめるには、わざとlintに違反するコードを書かせ、結果を見ます。
block.jsonの編集をClaude Codeに任せる
雛形のblock.jsonには、editorStyle・style・viewScriptなどが入っています。チュートリアルの例では、色と文字サイズをsupportsに任せ、独自のCSSもフロントのJavaScriptも使わないため、これらの行を削っています。
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "create-block/copyright-date-block",
"version": "0.1.0",
"title": "Copyright Date Block",
"category": "widgets",
"description": "Display your site's copyright date.",
"example": {},
"supports": {
"color": { "background": false, "text": true },
"html": false,
"typography": { "fontSize": true }
},
"textdomain": "copyright-date-block",
"editorScript": "file:./index.js",
"render": "file:./render.php"
}指示は「アイコンを外して説明文を差し替え、色と文字サイズをsupportsで有効にして」のように、block.jsonの項目に対応させて出します。textをtrueにすると背景色も既定で有効になるため、不要なら"background": falseを明示します。
編集後は、エディターを再読み込みして次の3点を目で確かめます。
- 設定サイドバーに、ColorとTypographyのパネルが出ているか
- ブロックのアイコンと説明が変わっているか
- ブロックのスタイルが、使用中のテーマに揃っているか
属性を足すなら、同じblock.jsonにattributesを書きます。typeは必須で、年を表示するかどうかはboolean、開始年はstringです。
"attributes": {
"showStartingYear": { "type": "boolean" },
"startingYear": { "type": "string" }
}属性名はedit.js・save.js・render.phpの3か所で同じ綴りを使います。綴りが1文字ずれても、ビルドは通ってしまいます。画面で値が反映されるかを確かめて初めて気づく類の不具合なので、実機確認の対象に入れます。
動的レンダリングと静的レンダリングを検証する
ブロックの描画は、動的、静的、その両方の3通りです。動的ブロックは、DBにブロックのコメント構文と属性だけを保存し、表示のたびにrender.phpがHTMLを作ります。静的ブロックは、save()が返したHTMLそのものをDBに保存します。
render.phpでは3つの変数が使えます。
| 変数 | 型 | 内容 |
|---|---|---|
$attributes | 型配列 | 内容ブロックの属性 |
$content | 型文字列 | 内容ブロックの既定のコンテンツ |
$block | 型WP_Block | 内容ブロックのインスタンス |
動的ブロックに静的な保存を足すと、プラグインが無効になってもコンテンツが残ります。本文は「カスタムHTML」ブロックに戻るため、段落ブロックへ変換できます。ブロックパターンやテーマのテンプレートに含まれる場合も、プラグインなしで内容が表示されます。
ここが、Claude Codeに書かせた後でいちばん確認が要る箇所です。公式チュートリアルでは、save()の中で現在の年を直接求めると、年が変わったときにエディターでブロック検証エラーが出ると説明されています。DBには前年のHTMLが保存されているのに、save()は新しい年のHTMLを返すためです。
回避策は、現在の年を属性(fallbackCurrentYear)として保存する形です。edit.jsのuseEffectで、保存済みの値と現在の年が違うときだけsetAttributesを呼びます。render.phpでは、2つの年が一致すれば$contentをそのまま使い、違うときだけ組み立て直します。
この仕組みは、日付が変わらないと再現しません。検証の観点を先に決めておきます。
- 保存したブロックを、エディターで開き直してもエラーが出ないか
- 属性を切り替えたとき、エディターの表示とフロントの表示が一致するか
- プラグインを無効にしても、投稿の内容が読めるか
3つ目はwp-envで確かめやすい項目です。プラグインを有効化したまま投稿を作り、いったん無効にして、投稿を表示します。
フロントエンドの表示をコマンドで確かめる
エディターの確認は目視になりますが、フロントの出力はコマンドでも見られます。wp-env runでコンテナ内のWP-CLIを呼べるので、ブロックを含む固定ページを作ります。次はその一例です(wp post createの使い方は公式の例に沿っています)。
wp-env run cli "wp post create --post_type=page --post_status=publish \
--post_name=block-check --post_title='Block check' \
--post_content='<!-- wp:create-block/copyright-date-block /-->'"
curl -s "http://localhost:8888/?pagename=block-check" | grep "©"ブロックのコメント構文に書く名前は、block.jsonのname(create-block/copyright-date-block)と一致させます。名前が違えば、ブロックは描画されません。表示された年が期待と合うか、grepの結果で判断できます。
この一連の流れをClaude Codeに任せる指示は、「npm run buildを通し、wp-envで固定ページを作って、フロントの出力に©と年が出ることを確認して」の1文で足ります。確認結果の出力を報告させると、通ったかどうかを読み取れます。
事前に知っておく条件
- 環境: コードエディター、Node.jsの開発ツール、ローカルのWordPress環境の3つが前提です。
wp-envを使わず、自分のWordPress環境のplugins/フォルダでcreate-blockを実行しても構いません。 - WordPressのバージョン:
wp-scriptsで作ったプラグインは、WordPress 6.8以降ならwp_register_block_types_from_metadata_collection()で全ブロックを1回の呼び出しで登録できます。古い環境ではregister_block_type()で個別に登録します。 - Ubuntu: 20.04.1より前のUbuntuでは、DockerとDocker Composeを別に設定する必要があります。
よくあるつまずき
Error while running docker-compose commandが出る: Docker Desktopが起動していないのが典型です。起動を確認し、ダッシュボードのログを見てから、もう一度wp-env startを実行します。
Host is already in use by another containerが出る: 同じコンテナか別のコンテナが動いています。起動したフォルダでwp-env stopを実行します。起動元を忘れたときはdocker stop $(docker ps -q)で全コンテナを止められますが、無関係なコンテナも止まるため注意が必要です。
エディターに変更が出ない: npm run startを止めた状態や、ビルドを走らせていない状態だと、build/が古いままです。src/を直したのに画面が変わらないときは、まずビルドの実行状態を疑います。
save.jsを足したのに、既存の投稿でエラーになる: すでに保存済みの動的ブロックが、新しいsave()の出力と食い違うためです。チュートリアルでは「ブロックの復旧を試行」を選んで更新する手順を踏んでいます。検証用の投稿を作り直すほうが、原因を切り分けやすくなります。
コンテナの状態がおかしい: wp-env clean allでデータベースを初期化し、それでも直らなければwp-env destroyで環境ごと作り直します。
まとめ
Gutenbergブロックの開発では、Claude Codeに書かせる部分と、実機で確かめる部分を分けるのが基本です。create-blockの雛形でファイルの役割が決まるため、指示はblock.jsonの項目やファイル単位で出せます。検証は、npm run buildとnpm run lint:jsで機械的に、wp-envの実機で見た目と保存の挙動を確かめます。
CLAUDE.mdに検証コマンドと約束を置き、lintはhookで強制すると、毎回の指示が短くなります。最も手間がかかるのは、動的ブロックに静的な保存を足す場面です。年のように時間で変わる値がsave()に入ると、後日になって検証エラーが出ます。属性に値を保存する設計になっているか、レビューで見る価値があります。
Officeアドインのようにmanifestとサイドロードで確かめる開発はClaude CodeでOfficeアドインを開発する手順、編集ごとの検証をhookで回す考え方はPostToolUse hookのLint自動修正でも扱っています。