wp-block-development について
WordPress ブロックエディター開発向けのコードレビュースキルで、PHP と JavaScript/React の実装を確認します。ファイル、行番号、重大度ごとに問題を整理し、BAD/GOOD のコード例を示します。
主な機能
- 01block.json スキーマと属性の検証
- 02React エディターコンポーネントの確認
- 03PHP サーバーサイドレンダリングの監査
- 04ブロックの廃止と移行の確認
- 05Interactivity API の利用確認
- 06重大度別のレビュー報告
活用シーン
- 01単一・複数ブロックプラグインのレビュー
- 02ブロック検証エラーの調査
- 03動的ブロックのレンダリング確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ jorgerosal/ wordpress-skills --skill wp-block-developmentインストール後、Claude Code で/wp-block-developmentを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/block-json-guide.mddynamic-blocks-guide.mdeditor-patterns.mdinteractivity-api-guide.md詳細
WordPress ブロック開発レビュースキル
概要
WordPress 6.x 以降のブロックエディター(Gutenberg)向けに、体系的なブロック開発レビューを行います。基本原則: WordPress のブロックは、エディター用の React コンポーネント(edit 関数)と、フロントエンド用の静的 HTML(save 関数)またはサーバーサイド PHP(render_callback/render ファイル)による二重アーキテクチャのパターンに従います。block.json が信頼できる唯一の情報源です。レビューでは、block.json のスキーマ、エディターのパターン(React/JSX)、サーバーサイドレンダリング(PHP)、属性の処理、非推奨機能の管理、Interactivity API の使用状況を検証します。検出事項はファイルごとにまとめ(実際のパスに基づき、PHP ファイルと JS/JSX ファイルを混在させて記載)、行番号、重大度ラベル(CRITICAL/WARNING/INFO)、BAD/GOOD のコード例を示します。
注: このスキルでは、PHP と JavaScript/React の両方のコードをレビューします。PHP は WordPress PHP Coding Standards(括弧内にはスペース、[] ではなく array()、Yoda 条件式)に従います。JavaScript/JSX は WordPress JS coding standards(タブによるインデント、JSDoc コメント、変数/関数には camelCase、コンポーネントには PascalCase)に従います。
使用する場面
次の場合に使用します:
- ブロックプラグインのコードレビュー(単一ブロック、複数ブロック、ブロックライブラリ)
- block.json のスキーマ検証とフィールド確認
- エディターコンポーネントのレビュー(edit/save 関数、React/JSX のパターン)
- レンダーコールバックまたはレンダーファイルの監査(サーバーサイド PHP)
- InnerBlocks パターンのレビュー(入れ子のブロック、テンプレート、templateLock)
- ブロックの非推奨機能の確認(save 関数の移行)
- Interactivity API ディレクティブのレビュー(WP 6.5 以降のフロントエンド操作)
- ブロック属性スキーマの検証(type、source、selector、default)
- @wordpress/scripts のビルド設定の確認
- ブロックの検証エラーの調査
- useBlockProps、RichText、InspectorControls、BlockControls の使用状況
次の場合には使用しないでください:
- theme.json の設定(利用可能な場合は wp-theme-development を使用)
- 一般的な React アプリケーションのレビュー(これはブロックエディター専用です)
- WooCommerce ブロックの拡張(利用可能な場合は wp-woocommerce-dev を使用)
- セキュリティのみの監査(包括的なセキュリティ分析には wp-security-review を使用)
- プラグインアーキテクチャの監査(プラグイン構造の確認には wp-plugin-development を使用)
- パフォーマンスのみの監査(wp-performance-review を使用)
コードレビューのワークフロー
体系的なブロックレビューを行うため、次の 7 つの手順に従います。
-
ブロックの種類とコンテキストを特定する
- 単一ブロックのプラグイン → block.json が 1 つで、シンプルな構成
- 複数ブロックのプラグイン → src/block-one/、src/block-two/ に複数のブロック
- ブロックライブラリ/コレクション → パッケージとして公開されるため、名前空間が重要
- テーマブロック → functions.php で登録され、読み込みコンテキストが異なる
-
block.json のスキーマを検証する(BLK-02、BLK-06、BLK-08、BLK-10)
- apiVersion は 3 である必要があります(WP 6.3 以降)。1 または 2 の場合は、アップグレード方法を案内して WARNING として報告します。
- name は「namespace/block-name」形式(小文字、英字、数字、ハイフン)である必要があります。
- title と category は必須です。
- attributes:type、source、selector、default の値を検証します。type の不一致を報告します。
- supports:color、spacing、typography、align、anchor、html
- editorScript、script、viewScript、viewScriptModule:「file:./path」形式を検証します。
- style、editorStyle:パスを検証します。
- render:動的ブロックでは「file:./render.php」を指定します。
- 検証用に $schema フィールドを推奨します。
-
edit 関数を確認する(BLK-05、BLK-20)
- useBlockProps() を呼び出し、ラッパー要素に展開する必要があります。
- インポート方法:@wordpress/* パッケージ(GOOD)と window.wp.*(BAD — レガシー)
- サイドバー設定には InspectorControls、ツールバーには BlockControls を使用します。
- RichText を適切に使用します(tagName、value、onChange、allowedFormats)。
- allowedBlocks と template のプロパティを指定して InnerBlocks を使用します。
- @wordpress/data の useSelect/useDispatch を使用します(複数のセレクターは 1 回の useSelect 呼び出しにまとめます)。
- i18n:ユーザーに表示するすべての文字列を、text domain とともに @wordpress/i18n の __() で囲みます。
-
保存関数またはレンダーコールバックを確認
- 静的ブロック: useBlockProps.save() を呼び出す必要があります。リッチテキストには RichText.Content、ネストされたブロックには InnerBlocks.Content を使用します
- 動的ブロック: save は null を返します(ネストされたブロックを使用する場合は InnerBlocks.Content を返します。BLK-14 重大)
- 保存関数は決定的でなければなりません。ランダムな値、Date.now()、副作用は使用しません
- レンダーコールバック/render.php: ラッパーには get_block_wrapper_attributes() を使用し、すべての出力をエスケープします(esc_html、esc_attr、esc_url、wp_kses_post。ただし、InnerBlocks の $content には適用しません。BLK-21)。先頭に defined( 'ABSPATH' ) || exit; を記述します
-
重大なパターンをスキャン
- apiVersion がない、または 3 ではなく 1/2 を使用している
- JS/JSX で @wordpress/* のインポートではなく window.wp.* を使用している
- 保存関数に useBlockProps.save() がない(apiVersion 3)
- レンダーコールバックで $content パラメーターに wp_kses_post() を使用している(埋め込みが壊れます)
- InnerBlocks を使用する動的ブロックで、save が null を返している(InnerBlocks.Content がない)
- source: 'meta' を指定した属性(非推奨。useEntityProp を使用します)
- 属性の型/source の組み合わせが無効
- PHP に register_block_type() の呼び出しがない
-
警告パターンを確認
- block.json に supports フィールドがない
- edit 関数に useBlockProps() がない
- JS/JSX または PHP で、文字列が __() で囲まれていない
- render.php に get_block_wrapper_attributes() がない
- PHP のレンダーファイルに defined( 'ABSPATH' ) のチェックがない
- 保存関数に副作用がある(Math.random、Date.now)
- block.json に $schema フィールドがない
- useSelect を複数回個別に呼び出している(パフォーマンス上のアンチパターン)
-
情報として改善点を記録
- block.json のレンダーファイルではなく、PHP で render_callback を使用している
- block.json に viewScript/viewScriptModule がない(フロントエンド JS がない)
- apiVersion 2(3 へのアップグレードを提案)
- block.json に keywords がない
- 自動挿入の機会にブロックフックを使用していない
以下の出力形式で報告します。セキュリティ上の懸念(エスケープされていないレンダー出力、Interactivity API の state に含まれるユーザー入力)が見つかった場合は、次の注記を追加します。「セキュリティ上の問題が検出されました。包括的なセキュリティ分析を行うには、/wp-sec-review を実行してください。」プラグインのアーキテクチャ上の問題(init フックでの登録、ABSPATH チェックの欠落)が見つかった場合は、次の注記を追加します。「プラグインのアーキテクチャ上の問題が検出されました。包括的なプラグインレビューを行うには、/wp-plugin-review を実行してください。」
ソースとビルドのレビュー: コードパターンの確認には src/ ファイルを使用します(開発者の意図)。build/ ディレクトリがない、または古い場合は、その旨を指摘します(index.asset.php のタイムスタンプと src/ の更新時刻を確認)。build/ ファイルのコード品質はレビューしないでください。これらはコンパイル済みの出力です。
ファイルタイプ別の確認項目
block.json (BLK-02, BLK-06, BLK-08, BLK-10)
apiVersion フィールド:
- CRITICAL:apiVersion がない → ブロックが正しく登録されません
- WARNING:apiVersion が 1 または 2 → iframe 分離のため、3 にアップグレードしてください(WP 6.3+)
- パターン:
"apiVersion": 3
name フィールド:
- CRITICAL:name がない → ブロックが登録されません
- CRITICAL:形式が無効("namespace/block-name" ではない)→ 登録に失敗します
- パターン:
"name": "my-plugin/my-block"(小文字、ハイフン、英字、数字)
attributes フィールド:
- CRITICAL:型が無効(string/number/boolean/object/array/integer/null ではない)→ 検証に失敗します
- CRITICAL:source:'meta' → 非推奨です。代わりに useEntityProp フックを使用してください
- WARNING:type が source のデータ型と一致しない → 属性が正しく設定されません
- パターン:type、source、selector、default の組み合わせを検証します
- ソース:attribute、text、html、query(meta は非推奨)
supports フィールド:
- WARNING:supports がない → ユーザーが色、余白、タイポグラフィをカスタマイズできません
- INFO:一般的な supports(color、spacing、typography、align、anchor)を追加できます
- パターン:各サポートタイプの設定をネストしたオブジェクト
editorScript/script/viewScript/viewScriptModule フィールド:
- WARNING:パス形式が無効 → アセットが読み込まれません
- パターン:
"file:./index.js"(block.json の場所からの相対パス) - 注:Interactivity API には viewScriptModule を使用します(WP 6.5+)
render フィールド:
- INFO:動的ブロックでは render_callback の代わりに render ファイルを使用できます
- パターン:
"file:./render.php"(block.json の場所からの相対パス)
$schema フィールド:
- INFO:$schema がない → IDE でスキーマを検証できません
- パターン:
"$schema": "https://schemas.wp.org/trunk/block.json"
Edit 関数 / edit.js (BLK-05, BLK-20)
useBlockProps の使用:
- CRITICAL:useBlockProps() が呼び出されていない → エディターでブロックが正しく表示されません
- CRITICAL:useBlockProps の結果がラッパーに展開されていない → ブロックのクラスや属性が不足します
- パターン:
const blockProps = useBlockProps();の後に<div { ...blockProps }>
インポートのパターン:
- WARNING:window.wp.* グローバルへのアクセス → レガシーなパターンであり、最新のビルドで問題が発生します
- パターン:推奨:
import { useBlockProps } from '@wordpress/block-editor';非推奨:const { useBlockProps } = window.wp.blockEditor;
InspectorControls と BlockControls:
- INFO:InspectorControls で設定サイドバーを追加できます
- INFO:BlockControls でツールバーコントロールを追加できます
- パターン:PanelBody/ToggleControl/SelectControl には InspectorControls、AlignmentToolbar/ToolbarGroup には BlockControls を使用します
RichText の使用:
- WARNING:tagName を指定せずに RichText を使用 → 正しく表示されない可能性があります
- WARNING:value/onChange を指定せずに RichText を使用 → 制御コンポーネントになりません
- パターン:
<RichText tagName="p" value={ attributes.content } onChange={ ( content ) => setAttributes( { content } ) } />
InnerBlocks の使用:
- INFO:ネストを制限するため、allowedBlocks の使用を検討してください
- INFO:デフォルトのブロック構造を定義するため、template の使用を検討してください
- パターン:
<InnerBlocks allowedBlocks={ [ 'core/paragraph' ] } template={ [ [ 'core/heading' ] ] } />
useSelect/useDispatch のパフォーマンス:
- WARNING:useSelect を個別に複数回呼び出す → ブロック数が多い場合、パフォーマンスが低下します
- パターン:複数の値を読み取る場合は、セレクターを単一の useSelect にまとめます
国際化:
- WARNING:__() を使わないハードコード文字列 → 翻訳できません
- パターン:ユーザーに表示するすべての文字列に
__( 'Text', 'text-domain' )を使用します
保存関数/save.js(BLK-05、BLK-07)
静的ブロック(save が JSX を返す場合):
- CRITICAL:useBlockProps.save() がない → ブロックの検証エラー(apiVersion 3)
- WARNING:RichText.Content を使わずに RichText を使用 → リッチテキストが正しく保存されません
- WARNING:InnerBlocks.Content を使わずに InnerBlocks を使用 → ネストされたブロックが保存されません
- パターン:
const blockProps = useBlockProps.save();を記述してから、<div { ...blockProps }>
動的ブロック(save が null または InnerBlocks.Content を返す場合):
- CRITICAL:InnerBlocks を含む動的ブロックで save が null を返す → ネストされたブロックが失われます(BLK-14)
- パターン:ブロックで InnerBlocks を使用する場合、動的ブロックであっても save は必ず
<InnerBlocks.Content />を返す必要があります
決定論的な保存:
- WARNING:save で Math.random() または Date.now() を使用 → 再保存時にブロックの検証エラーが発生します
- WARNING:save で副作用が発生 → 動作が予測不能になります
- パターン:同一の属性に対して、save は常に同一のマークアップを返す必要があります
レンダリングコールバック/render.php(BLK-11)
ABSPATH のチェック:
- WARNING:defined( 'ABSPATH' ) || exit; がない → ファイルへの直接アクセスが可能になります
- パターン:<?php の直後の最初の行に ABSPATH のチェックを記述します
- 相互参照:セキュリティ対策の詳細については、wp-security-review を参照してください
get_block_wrapper_attributes():
- WARNING:get_block_wrapper_attributes() の代わりにクラスを手動で連結 → ブロックのサポート機能が適用されません
- パターン:
$wrapper_attributes = get_block_wrapper_attributes();を記述してから、<div <?php echo $wrapper_attributes; ?>>
出力のエスケープ:
- CRITICAL:エスケープされていない出力 → XSS の脆弱性(CWE-79)
- CRITICAL:InnerBlocks の $content に wp_kses_post() を使用 → 埋め込みが壊れます(BLK-21)
- パターン:テキストには esc_html()、属性には esc_attr()、URL には esc_url()、信頼できる HTML には wp_kses_post() を使用します(ただし、InnerBlocks の $content には使用しません)
- 相互参照:包括的なエスケープパターンについては、wp-security-review を参照してください
関数のパラメーター:
- $attributes:ブロック属性の配列
- $content:InnerBlocks の HTML(InnerBlocks を使用する場合)
- $block:コンテキストやその他のメタデータを含む WP_Block インスタンス
- パターン:動的ブロックの機能をすべて活用するには、3 つのパラメーターをすべて使用します
プラグインのメインファイル/plugin.php
register_block_type():
- CRITICAL:register_block_type() がない → ブロックが登録されません
- WARNING:'init' にフックされていない → 登録のタイミングが早すぎる可能性があります
- パターン:
add_action( 'init', 'callback' );を記述してから、register_block_type( __DIR__ . '/build/block-name' );
複数ブロックの登録:
- WARNING:複数ブロックのプラグインで register_block_type() が 1 つだけ → ほかのブロックが登録されません
- パターン:各ブロックの build ディレクトリに対して register_block_type() を呼び出します
プラグインヘッダーと ABSPATH:
- 相互参照:プラグインヘッダーの要件と ABSPATH のチェックについては、wp-plugin-development を参照してください
ブロックの非推奨化(BLK-09)
deprecated 配列:
- CRITICAL:非推奨化を設定せずに保存関数を変更 → 既存コンテンツでブロックの検証エラーが発生します
- WARNING:非推奨化のエントリーに apiVersion がない → 非推奨化が一致しない可能性があります
- パターン:バージョンごとに save と attributes を含む deprecated 配列を設定します
migrate 関数:
- INFO:属性スキーマの変更に migrate 関数を追加できます
- パターン:
migrate( attributes ) { return { ...attributes, newField: 'default' }; }
順序:
- WARNING:非推奨化の順序が古いものからになっている → WordPress は新しいものから確認するため、パフォーマンスの問題が発生します
- パターン:配列では新しい非推奨化を最初に記述します
Interactivity API ファイル(BLK-12)
WP 6.5+ バージョンマーカー:
- INFO:Interactivity API には WordPress 6.5+ が必要です → バージョンチェックまたはプラグイン要件を追加します
- パターン:プラグインヘッダーで
Requires at least: 6.5を確認します
render.php のディレクティブ:
- data-wp-interactive="namespace" → Interactivity API のスコープに必須です
- wp_interactivity_state() → サーバー側の状態を初期化します
- wp_interactivity_config() → サーバー側の設定を行います
- data-wp-context → ネストされた要素のコンテキストプロバイダーです
- ディレクティブ:data-wp-bind、data-wp-on、data-wp-class、data-wp-style、data-wp-text、data-wp-watch、data-wp-init、data-wp-each
view.js(フロントエンドストア):
- WARNING:window.wp.interactivity からインポートしています → レガシーパターンです。@wordpress/interactivity を使用してください
- パターン:
import { store } from '@wordpress/interactivity';の後にstore( 'namespace', { actions, state } );を記述します
主な違い:
- Interactivity API = フロントエンド専用(view.js、render.php のディレクティブ)
- エディターでは引き続き React を使用します(edit.js)
- フロントエンドの状態管理とエディターコンポーネントの状態を混同しないでください
セキュリティ上の関連事項:
- WARNING:wp_interactivity_state() でユーザー入力をサニタイズせずに使用しています → XSS のリスクがあります
- 関連項目:入力のサニタイズパターンについては wp-security-review を参照してください
ブロックコンテキスト(BLK-16)
providesContext(親ブロック):
- パターン:親の block.json に
"providesContext": { "myPlugin/keyName": "attributeName" }を記述します - コンテキストキーには名前空間を付けます:「myPlugin/keyName」形式を使用します
usesContext(子ブロック):
- パターン:子の block.json に
"usesContext": [ "myPlugin/keyName" ]を記述します - edit でのアクセス方法:
function Edit( { context } ) { const value = context['myPlugin/keyName']; }
表層レベルのチェック
ブロック変換(BLK-13 一部):
- INFO:他のブロックとの相互変換用に transforms を追加できます
- パターン:ブロック登録時に from/to 配列を指定します
ブロックバリエーション(BLK-13):
- INFO:プリセット設定用にバリエーションを追加できます
- パターン:variation picker、isDefault、scope プロパティ
ブロックスタイル:
- INFO:block.json または PHP でスタイルバリエーションを登録できます
- パターン:block.json の styles 配列、または PHP の register_block_style()
ブロックパターン(BLK-18):
- パターン:register_block_pattern() の構造、カテゴリー、キーワード
- 注:パターンは複数ブロックのレイアウトです。単一ブロックのプリセットであるバリエーションとは異なります
ブロックフック(BLK-19):
- INFO:自動挿入に blockHooks を使用できます(WP 6.4+)
- CRITICAL:ブロックフックは動的ブロック(save が null を返す)でのみ動作します
- パターン:block.json の blockHooks プロパティ
Block Bindings API(BLK-17):
- INFO:動的な属性接続にバインディングを使用できます(WP 6.5+)
- パターン:表層レベルの検出のみ
@wordpress/scripts のビルド設定:
- WARNING:@wordpress/scripts がありません → ビルドツールチェーンがありません
- WARNING:package.json に build/start スクリプトがありません → ブロックをコンパイルできません
- パターン:package.json で
"scripts": { "build": "wp-scripts build", "start": "wp-scripts start" }を確認します
テンプレートロック:
- INFO:固定レイアウトには InnerBlocks で templateLock を使用できます
- パターン:
<InnerBlocks templateLock="all" />はブロックの追加、削除、並べ替えを防止します
すばやく検出するための検索パターン(BLK-22)
ブロックをすばやくスキャンするには、以下の rg コマンドを使用します。重要度別に整理されています。PHP と JS/JSX の両方のファイルが対象です。
CRITICAL パターン
# block.json without apiVersion 3
rg -l --iglob 'block.json' . | xargs -I{} sh -c "rg -q '\"apiVersion\"\\s*:\\s*3' '{}' || echo '{}'"
# JS/JSX files with window.wp.* instead of @wordpress/* imports
rg -n "window\.wp\." . -g '*.{js,jsx}'
# save function without useBlockProps.save() in JSX files (manual candidate list)
rg -n "function save|const save\s*=|save:\s*\(" . -g '*.{js,jsx}'
# render callback using wp_kses_post on $content parameter (breaks embeds)
rg -n "wp_kses_post\s*\(\s*\$content\s*\)" . -g '*.php'
# Dynamic block with InnerBlocks but save returns null
# Manual check: if edit.js uses InnerBlocks, save.js should include InnerBlocks.Content.
rg -n "InnerBlocks" . -g '*.{js,jsx}'
rg -n "return\s+null" . -g '*.{js,jsx}'
# Attribute with source: 'meta' (deprecated)
rg -n "\"source\"\s*:\s*\"meta\"" . -g 'block.json'
# Missing register_block_type() call in plugin bootstrap files
rg -n "register_block_type\s*\(" . -g '*.php'
WARNING パターン
# block.json missing supports field
rg -l --iglob 'block.json' . | xargs -I{} sh -c "rg -q '\"supports\"' '{}' || echo '{}'"
# edit function without useBlockProps() in JS/JSX files (manual candidate list)
rg -n "function Edit|export default function Edit|const Edit\s*=" . -g '*.{js,jsx}'
# Hardcoded strings not wrapped in __() in JS/JSX files
rg -n "title\s*:\s*['\"][A-Z]" . -g '*.{js,jsx}'
# render.php without get_block_wrapper_attributes()
rg -l --iglob 'render.php' . | xargs -I{} sh -c "rg -q 'get_block_wrapper_attributes' '{}' || echo '{}'"
# PHP render files without defined( 'ABSPATH' ) check
rg -l --iglob 'render.php' . | xargs -I{} sh -c "rg -q 'defined.*ABSPATH' '{}' || echo '{}'"
# save function with side effects (Math.random, Date.now)
rg -n "Math\.random|Date\.now|new Date\(" . -g 'save.js'
# Missing block.json $schema field
rg -l --iglob 'block.json' . | xargs -I{} sh -c "rg -q '\"\\$schema\"' '{}' || echo '{}'"
# useSelect with multiple separate calls (performance anti-pattern)
# Manual check: inspect files with repeated useSelect() calls.
rg -n "useSelect\s*\(" . -g '*.{js,jsx}'
INFO パターン
# Using render_callback in PHP instead of render file in block.json
grep -rn "render_callback" --include="*.php" .
# Missing viewScript/viewScriptModule in block.json (no frontend JS)
grep -L "viewScript\|viewScriptModule" --include="block.json" .
# apiVersion 2 (suggest upgrade to 3)
grep -rn "\"apiVersion\": 2" --include="block.json" .
# Missing keywords in block.json
grep -L "\"keywords\"" --include="block.json" .
# Not using block hooks for auto-insertion opportunities
# (Manual check: Review block purpose for auto-insertion potential)
注: JavaScript の grep パターンには、PHP のパターンとは異なる正規表現が必要です。JS ファイルには --include="*.js" --include="*.jsx"、PHP ファイルには --include="*.php" を使用します。
ブロックコンテキストの検出
ブロックの分布と読み込みコンテキストに基づいた、コンテキスト対応のレビュー注記です。
単一ブロックのプラグイン
構成: ルートまたは src/ に block.json が 1 つあり、ビルド出力も 1 つです レビュー時の調整: 標準的なレビューで、特別な考慮事項はありません パターン: プラグインフォルダーに index.js、edit.js、save.js を含む src/ ディレクトリが 1 つあります
複数ブロックのプラグイン
構成: src/block-one/、src/block-two/ などに複数のブロックがあり、それぞれに block.json がある レビュー時の調整:
- src/shared/ または src/components/ に共有コンポーネントがあるか確認します
- すべてのブロックで命名規則が一貫しているか確認します
- ビルド設定がすべてのブロックに対応しているか確認します(複数のエントリーポイントまたはワイルドカード)
- 各ブロックのビルドディレクトリに対して register_block_type() が呼び出されているか確認します パターン: プラグインフォルダーに複数の src/*/block.json ファイルが含まれます
ブロックライブラリ/コレクション
構成: 複数のブロックを含む npm パッケージまたは WordPress.org プラグインとして公開されます レビュー時の調整:
- 名前空間は非常に重要です(すべてのブロックで一貫した名前空間を使用する必要があります)
- 命名が一貫しているか確認します:「namespace/block-one」、「namespace/block-two」
- すべてのブロックが同じ名前空間にあるか確認します
- package.json に、ビルドエントリーポイントを指す「main」フィールドが必要です パターン: package.json に「name」フィールドがあり、npm または WordPress.org に公開されます
テーマブロック
構成: functions.php で登録され、ブロックファイルは /blocks/ または /inc/blocks/ にあります レビュー時の調整:
- 読み込みコンテキストが異なります(テーマの有効化とプラグインの有効化)
- ブロックが theme.json の設定(色、間隔、タイポグラフィ)に依存する場合があります
- theme.json との連携について簡単に触れ、詳細はフェーズ 4(テーマ開発)に委ねます
- ブロックが 'init' または 'after_setup_theme' フックで登録されているか確認します パターン: functions.php に register_block_type() があり、テーマのサブディレクトリにブロックがあります
クイックリファレンス:ブロック開発パターン(BLK-23)
懸念事項別に整理した、よく使われるブロックパターンです。すべての例で WordPress のコーディング標準を使用しています(PHP:括弧内のスペース、[] ではなく array()、ヨーダ条件式。JS:タブによるインデント、camelCase、コンポーネントには PascalCase)。
block.json スキーマ
すべてのフィールドに注釈を付けた完全なスキーマ(apiVersion 3):
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "my-plugin/my-block",
"title": "My Block",
"category": "widgets",
"icon": "smiley",
"description": "A custom block example",
"keywords": [ "custom", "example" ],
"version": "1.0.0",
"textdomain": "my-plugin",
"attributes": {
"content": {
"type": "string",
"source": "html",
"selector": "p",
"default": ""
},
"showImage": {
"type": "boolean",
"default": false
}
},
"supports": {
"html": false,
"color": {
"background": true,
"text": true
},
"spacing": {
"margin": true,
"padding": true
},
"typography": {
"fontSize": true,
"lineHeight": true
},
"align": [ "wide", "full" ],
"anchor": true
},
"editorScript": "file:./index.js",
"editorStyle": "file:./index.css",
"style": "file:./style-index.css",
"viewScriptModule": "file:./view.js"
}
ブロックの登録
PHP + JS の登録パターン:
❌ BAD:ABSPATH チェックがなく、init にフックされていません
<?php
function my_plugin_register_block() {
register_block_type( __DIR__ . '/build/my-block' );
}
my_plugin_register_block(); // Runs immediately, not on init
✅ GOOD:ABSPATH と init フックを含む完全な登録
<?php
defined( 'ABSPATH' ) || exit;
function my_plugin_register_blocks() {
register_block_type( __DIR__ . '/build/my-block' );
}
add_action( 'init', 'my_plugin_register_blocks' );
✅ GOOD:複数ブロックの登録
<?php
defined( 'ABSPATH' ) || exit;
function my_plugin_register_blocks() {
register_block_type( __DIR__ . '/build/block-one' );
register_block_type( __DIR__ . '/build/block-two' );
register_block_type( __DIR__ . '/build/block-three' );
}
add_action( 'init', 'my_plugin_register_blocks' );
静的ブロックパターン
useBlockProps を使って編集し、useBlockProps.save、RichText + RichText.Content を使って保存します:
❌ BAD:編集と保存で useBlockProps がありません
// edit.js
export default function Edit( { attributes, setAttributes } ) {
return (
<div className="my-block">
<input
value={ attributes.content }
onChange={ ( e ) => setAttributes( { content: e.target.value } ) }
/>
</div>
);
}
// save.js
export default function save( { attributes } ) {
return (
<div className="my-block">
<p>{ attributes.content }</p>
</div>
);
}
✅ GOOD:useBlockProps と RichText を使った完全な静的ブロック
// edit.js
import { useBlockProps, RichText } from '@wordpress/block-editor';
export default function Edit( { attributes, setAttributes } ) {
const blockProps = useBlockProps();
return (
<div { ...blockProps }>
<RichText
tagName="p"
value={ attributes.content }
onChange={ ( content ) => setAttributes( { content } ) }
placeholder="Enter content..."
/>
</div>
);
}
// save.js
import { useBlockProps, RichText } from '@wordpress/block-editor';
export default function save( { attributes } ) {
const blockProps = useBlockProps.save();
return (
<div { ...blockProps }>
<RichText.Content tagName="p" value={ attributes.content } />
</div>
);
}
動的ブロックパターン
useBlockProps を使って編集し、保存では null(または InnerBlocks.Content)を返します。render.php では get_block_wrapper_attributes とエスケープを使います:
❌ BAD:ラッパー属性がなく、出力がエスケープされていない render コールバック
<?php
function my_plugin_render_callback( $attributes ) {
return '<div><p>' . $attributes['content'] . '</p></div>';
}
✅ GOOD:適切なエスケープを含む完全な動的ブロック
<?php
// render.php
defined( 'ABSPATH' ) || exit;
$wrapper_attributes = get_block_wrapper_attributes();
?>
<div <?php echo $wrapper_attributes; ?>>
<p><?php echo esc_html( $attributes['content'] ); ?></p>
</div>
// save.js - Dynamic blocks return null
export default function save() {
return null;
}
✅ GOOD:render ファイルを指定して登録された動的ブロック
<?php
// plugin.php
defined( 'ABSPATH' ) || exit;
function my_plugin_register_blocks() {
register_block_type( __DIR__ . '/build/my-block' );
// render file specified in block.json "render": "file:./render.php"
}
add_action( 'init', 'my_plugin_register_blocks' );
InnerBlocks パターン
allowedBlocks、template、templateLock、および save 内の InnerBlocks.Content(動的ブロックの場合も同様):
❌ BAD:InnerBlocks を使用しているのに save が null を返す動的ブロック
// edit.js
import { useBlockProps, InnerBlocks } from '@wordpress/block-editor';
export default function Edit() {
const blockProps = useBlockProps();
return (
<div { ...blockProps }>
<InnerBlocks />
</div>
);
}
// save.js - CRITICAL: Nested blocks will be lost!
export default function save() {
return null;
}
✅ GOOD:ネストされたコンテンツを保存する InnerBlocks 付きの動的ブロック
// edit.js
import { useBlockProps, InnerBlocks } from '@wordpress/block-editor';
export default function Edit() {
const blockProps = useBlockProps();
const ALLOWED_BLOCKS = [ 'core/paragraph', 'core/image', 'core/heading' ];
const TEMPLATE = [
[ 'core/heading', { placeholder: 'Card Title' } ],
[ 'core/paragraph', { placeholder: 'Card content...' } ],
];
return (
<div { ...blockProps }>
<InnerBlocks
allowedBlocks={ ALLOWED_BLOCKS }
template={ TEMPLATE }
templateLock={ false }
/>
</div>
);
}
// save.js - MUST save InnerBlocks.Content even for dynamic blocks
import { InnerBlocks } from '@wordpress/block-editor';
export default function save() {
return <InnerBlocks.Content />;
}
✅ GOOD:InnerBlocks の HTML に $content パラメーターを使う render.php
<?php
// render.php
defined( 'ABSPATH' ) || exit;
$wrapper_attributes = get_block_wrapper_attributes();
?>
<div <?php echo $wrapper_attributes; ?>>
<h3><?php echo esc_html( $attributes['title'] ); ?></h3>
<?php echo $content; // InnerBlocks HTML - DO NOT use wp_kses_post() ?>
</div>
useBlockProps
edit(useBlockProps())と save(useBlockProps.save())でカスタムプロパティをマージします:
❌ BAD:useBlockProps がありません
export default function Edit() {
return <div className="my-block">Content</div>;
}
function save() {
return <div className="my-block">Content</div>;
}
✅ GOOD:カスタムプロパティをマージした useBlockProps
import { useBlockProps } from '@wordpress/block-editor';
export default function Edit( { attributes } ) {
const blockProps = useBlockProps( {
className: 'my-custom-class',
style: { backgroundColor: attributes.bgColor },
} );
return <div { ...blockProps }>Content</div>;
}
function save( { attributes } ) {
const blockProps = useBlockProps.save( {
className: 'my-custom-class',
style: { backgroundColor: attributes.bgColor },
} );
return <div { ...blockProps }>Content</div>;
}
属性
type/source/selector/default のパターンと、属性の型の一致:
❌ BAD:型の不一致、デフォルト値なし、非推奨の source
{
"attributes": {
"count": {
"type": "number",
"source": "text",
"selector": ".count"
},
"metaValue": {
"type": "string",
"source": "meta",
"meta": "my_meta_key"
}
}
}
✅ GOOD:型と source の適切な組み合わせ、デフォルト値、最新のパターン
{
"attributes": {
"content": {
"type": "string",
"source": "html",
"selector": "p",
"default": ""
},
"count": {
"type": "number",
"source": "attribute",
"selector": ".count",
"attribute": "data-count",
"default": 0
},
"url": {
"type": "string",
"source": "attribute",
"selector": "a",
"attribute": "href",
"default": ""
},
"items": {
"type": "array",
"source": "query",
"selector": "li",
"query": {
"text": {
"type": "string",
"source": "text"
}
},
"default": []
}
}
}
✅ GOOD:useEntityProp を使ったメタ属性(非推奨の source ではありません)
import { useEntityProp } from '@wordpress/core-data';
export default function Edit() {
const [ meta, setMeta ] = useEntityProp( 'postType', 'post', 'meta' );
const value = meta.my_meta_key;
const updateMeta = ( newValue ) => {
setMeta( { ...meta, my_meta_key: newValue } );
};
// Use value and updateMeta in component
}
ブロックコントロール
サイドバーには InspectorControls、ツールバーには BlockControls、PanelBody、ToggleControl、SelectControl を使います:
❌ BAD:ブロックのマークアップに設定がハードコードされ、サイドバーコントロールがありません
export default function Edit( { attributes, setAttributes } ) {
return (
<div>
<label>
Show Image:
<input
type="checkbox"
checked={ attributes.showImage }
onChange={ ( e ) => setAttributes( { showImage: e.target.checked } ) }
/>
</label>
</div>
);
}
✅ GOOD:サイドバーには InspectorControls、ツールバーには BlockControls
import { useBlockProps, InspectorControls, BlockControls, AlignmentToolbar } from '@wordpress/block-editor';
import { PanelBody, ToggleControl, SelectControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
export default function Edit( { attributes, setAttributes } ) {
const blockProps = useBlockProps();
return (
<>
<BlockControls>
<AlignmentToolbar
value={ attributes.align }
onChange={ ( align ) => setAttributes( { align } ) }
/>
</BlockControls>
<InspectorControls>
<PanelBody title={ __( 'Settings', 'my-plugin' ) }>
<ToggleControl
label={ __( 'Show featured image', 'my-plugin' ) }
checked={ attributes.showImage }
onChange={ ( showImage ) => setAttributes( { showImage } ) }
/>
<SelectControl
label={ __( 'Display style', 'my-plugin' ) }
value={ attributes.style }
options={ [
{ label: 'Default', value: 'default' },
{ label: 'Card', value: 'card' },
{ label: 'List', value: 'list' },
] }
onChange={ ( style ) => setAttributes( { style } ) }
/>
</PanelBody>
</InspectorControls>
<div { ...blockProps }>
{/* Block content */}
</div>
</>
);
}
ブロックの非推奨化
deprecated 配列、save 関数と属性の移行、エントリー内の apiVersion、新しい順:
❌ BAD:非推奨化せずに save 関数を変更
// Changed save markup - existing blocks will show validation error
function save( { attributes } ) {
const blockProps = useBlockProps.save();
return <div { ...blockProps }>{ attributes.content }</div>;
}
✅ GOOD:古い save 関数の非推奨化エントリー
import { useBlockProps } from '@wordpress/block-editor';
const deprecated = [
{
// Version 2: Added useBlockProps (newest deprecation first)
apiVersion: 2,
attributes: {
content: { type: 'string' },
},
save( { attributes } ) {
return <div className="my-block">{ attributes.content }</div>;
},
},
{
// Version 1: Original block
apiVersion: 2,
attributes: {
content: { type: 'string' },
},
save( { attributes } ) {
return <p>{ attributes.content }</p>;
},
migrate( attributes ) {
// Transform old attributes to new schema
return {
...attributes,
newField: 'default',
};
},
},
];
// Current version
function save( { attributes } ) {
const blockProps = useBlockProps.save();
return <div { ...blockProps }>{ attributes.content }</div>;
}
export default {
edit: Edit,
save,
deprecated,
};
Interactivity API
data-wp-interactive、data-wp-bind、data-wp-on、store()、wp_interactivity_state()、フロントエンド専用であることを強調:
❌ BAD:フロントエンドのインタラクションにカスタム JavaScript を使用
<?php
// render.php
?>
<div class="my-block">
<button id="toggle-button">Toggle</button>
<div id="content" style="display: none;">Hidden content</div>
</div>
<script>
document.getElementById('toggle-button').addEventListener('click', function() {
var content = document.getElementById('content');
content.style.display = content.style.display === 'none' ? 'block' : 'none';
});
</script>
✅ GOOD:ディレクティブを使用した Interactivity API(WP 6.5+)
<?php
// render.php
defined( 'ABSPATH' ) || exit;
wp_interactivity_state( 'myPlugin', array(
'isOpen' => false,
) );
$wrapper_attributes = get_block_wrapper_attributes();
?>
<div
<?php echo $wrapper_attributes; ?>
data-wp-interactive="myPlugin"
data-wp-context='{ "id": "<?php echo esc_attr( uniqid() ); ?>" }'
>
<button data-wp-on--click="actions.toggle">
<?php esc_html_e( 'Toggle', 'my-plugin' ); ?>
</button>
<div data-wp-bind--hidden="!state.isOpen">
<p><?php esc_html_e( 'Hidden content', 'my-plugin' ); ?></p>
</div>
</div>
// view.js - Frontend store (NOT editor code)
import { store } from '@wordpress/interactivity';
store( 'myPlugin', {
state: {
isOpen: false,
},
actions: {
toggle: ( { state } ) => {
state.isOpen = ! state.isOpen;
},
},
} );
block.json の設定:
{
"viewScriptModule": "file:./view.js"
}
注: Interactivity API はフロントエンド専用です。エディターでは引き続き React(edit.js)を使用します。
インポートパターン
@wordpress/* パッケージのインポートと window.wp.* によるレガシーアクセス:
❌ BAD:window.wp. グローバルアクセス(レガシー)*
const { registerBlockType } = window.wp.blocks;
const { useBlockProps } = window.wp.blockEditor;
const { __ } = window.wp.i18n;
✅ GOOD:@wordpress/ パッケージのインポート(モダン)*
import { registerBlockType } from '@wordpress/blocks';
import { useBlockProps } from '@wordpress/block-editor';
import { __ } from '@wordpress/i18n';
ブロックサポート
block.json 内の color、spacing、typography、align、anchor:
❌ BAD:supports フィールドがない
{
"name": "my-plugin/my-block",
"title": "My Block"
}
✅ GOOD:完全な supports 設定
{
"name": "my-plugin/my-block",
"title": "My Block",
"supports": {
"html": false,
"color": {
"background": true,
"text": true,
"gradients": true,
"link": true
},
"spacing": {
"margin": true,
"padding": true,
"blockGap": true
},
"typography": {
"fontSize": true,
"lineHeight": true,
"fontFamily": true,
"fontWeight": true
},
"align": [ "wide", "full" ],
"anchor": true
}
}
ブロックバリエーション
attributes と innerBlocks を含む variation オブジェクト:
import { registerBlockVariation } from '@wordpress/blocks';
registerBlockVariation( 'core/columns', {
name: 'three-columns-equal',
title: 'Three Columns (Equal)',
description: 'Three columns with equal width',
icon: 'columns',
isDefault: false,
scope: [ 'block' ],
attributes: {
columns: 3,
},
innerBlocks: [
[ 'core/column' ],
[ 'core/column' ],
[ 'core/column' ],
],
} );
ブロック変換
from/to パターン:
import { createBlock } from '@wordpress/blocks';
export default {
edit: Edit,
save,
transforms: {
from: [
{
type: 'block',
blocks: [ 'core/paragraph' ],
transform: ( { content } ) => {
return createBlock( 'my-plugin/my-block', {
content,
} );
},
},
],
to: [
{
type: 'block',
blocks: [ 'core/paragraph' ],
transform: ( { content } ) => {
return createBlock( 'core/paragraph', {
content,
} );
},
},
],
},
};
ブロックコンテキスト
providesContext/usesContext:
// Parent block.json
{
"providesContext": {
"myPlugin/userId": "userId",
"myPlugin/userName": "userName"
},
"attributes": {
"userId": { "type": "number" },
"userName": { "type": "string" }
}
}
// Child block.json
{
"usesContext": [ "myPlugin/userId", "myPlugin/userName" ]
}
// Child edit.js
export default function Edit( { context } ) {
const userId = context['myPlugin/userId'];
const userName = context['myPlugin/userName'];
return (
<div>
<p>User: { userName } ({ userId })</p>
</div>
);
}
ブロックフック
動的ブロックのみで使用する blockHooks:
❌ BAD:静的ブロックでブロックフックを使用
{
"blockHooks": {
"core/post-content": "after"
}
}
// save.js - Static save = block hooks won't work
function save() {
return <div>Content</div>;
}
✅ GOOD:動的ブロックでブロックフックを使用
{
"blockHooks": {
"core/post-content": "after"
},
"render": "file:./render.php"
}
// save.js - Dynamic block (null save) = block hooks work
function save() {
return null;
}
@wordpress/scripts
package.json の scripts 設定:
❌ BAD:ビルドスクリプトがない
{
"scripts": {
"test": "echo \"No build toolchain configured\""
}
}
✅ GOOD:完全な @wordpress/scripts 設定
{
"scripts": {
"build": "wp-scripts build",
"start": "wp-scripts start",
"lint:js": "wp-scripts lint-js",
"format": "wp-scripts format",
"packages-update": "wp-scripts packages-update"
},
"devDependencies": {
"@wordpress/scripts": "^27.0.0"
}
}
重大度の定義(BLK-24)
| 重大度 | 定義 | 例 |
|---|---|---|
| CRITICAL | ブロックがレンダリングされない、エディターがクラッシュする、またはブロックの検証エラーが発生する | apiVersion がない、または移行パスなしで apiVersion 1/2 を使用している、save の検証エラーを引き起こす無効な属性型、非推奨化エントリーなしで save 関数が一致しない、apiVersion 3 のブロックに useBlockProps.save() がない、edit の InnerBlocks に対して save が null(InnerBlocks.Content ではない)を返す、InnerBlocks の $content パラメーターに wp_kses_post() を使用している、モダンなビルドを破損させる window.wp.* アクセス、register_block_type() の呼び出しがない、非推奨の source:'meta' 属性パターン |
| WARNING | ブロックは動作するものの、品質や互換性に問題がある、または標準外のパターンを使用している | 非推奨 API の使用(重大な問題はないが apiVersion 1/2 を使用)、ブロックサポート(color、spacing、typography)がない、save 関数の変更に対する非推奨化ハンドラーがない、JS/JSX または PHP 内に i18n を使用していないハードコード文字列がある、useSelect の呼び出しが複数に分かれている(パフォーマンスの問題)、render.php に get_block_wrapper_attributes() がない、PHP ファイルに defined( 'ABSPATH' ) がない、副作用のある save 関数(Math.random、Date.now) |
| INFO | ベストプラクティスの改善、または最適化の機会 | render_callback 関数の代わりに render PHP ファイルを使用できる、フロントエンドのインタラクションに viewScript/viewScriptModule がない、block.json の $schema フィールドを使用していない、apiVersion 2(3 へのアップグレードが可能)、block.json に keywords がない、自動挿入にブロックフックを活用できる、よく使うプリセット用のブロックバリエーションを追加できる、固定された InnerBlocks レイアウトにテンプレートロックがない |
出力形式(BLK-24)
ファイルごとに指摘事項をまとめて報告してください(PHP ファイルと JS/JSX ファイルは、実際のファイルパスに従って混在させます)。行番号と重大度ラベルを付け、各指摘事項には BAD/GOOD のコードペアを使用してください。
# WordPress Block Review: my-block-plugin
## FILE: src/index.js
### Line 3: WARNING - Legacy global access
window.wp.* access is legacy pattern. Use @wordpress/* package imports for modern build toolchain.
❌ **BAD:**
```javascript
const { registerBlockType } = window.wp.blocks;
✅ GOOD:
import { registerBlockType } from '@wordpress/blocks';
FILE: src/edit.js
Line 12: CRITICAL - useBlockProps がない
edit 関数で useBlockProps() が呼び出されていません。エディターでブロックが正しくレンダリングされません。
❌ BAD:
export default function Edit() {
return <div className="my-block">Content</div>;
}
✅ GOOD:
import { useBlockProps } from '@wordpress/block-editor';
export default function Edit() {
const blockProps = useBlockProps();
return <div { ...blockProps }>Content</div>;
}
FILE: src/save.js
Line 5: CRITICAL - useBlockProps.save() がない
apiVersion 3 のブロックには useBlockProps.save() が必要です。ブロックの検証エラーが発生します。
❌ BAD:
function save() {
return <div className="my-block">Content</div>;
}
✅ GOOD:
import { useBlockProps } from '@wordpress/block-editor';
function save() {
const blockProps = useBlockProps.save();
return <div { ...blockProps }>Content</div>;
}
FILE: render.php
Line 15: CRITICAL - InnerBlocks コンテンツでの wp_kses_post
$content に wp_kses_post() を使用すると、埋め込みブロックと oEmbed の処理が壊れます。InnerBlocks のコンテンツはすでにサニタイズされています。
❌ BAD:
<?php echo wp_kses_post( $content ); ?>
✅ GOOD:
<?php echo $content; // InnerBlocks already sanitized ?>
Line 1: WARNING - ABSPATH チェックの不足
直接アクセスを防ぐため、ファイルの先頭に ABSPATH チェックを追加してください。
❌ BAD:
<?php
$wrapper_attributes = get_block_wrapper_attributes();
✅ GOOD:
<?php
defined( 'ABSPATH' ) || exit;
$wrapper_attributes = get_block_wrapper_attributes();
FILE: block.json
Line 2: WARNING - apiVersion 2(アップグレード可能)
apiVersion 2 は引き続き動作しますが、iframe エディターの分離のため、3 へのアップグレードを推奨します(WP 6.3 以降)。
❌ BAD:
{
"apiVersion": 2
}
✅ GOOD:
{
"apiVersion": 3
}
Line 10: INFO - $schema フィールドの不足
IDE の検証サポートのため、$schema フィールドを追加してください。
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
...
}
SUMMARY
問題の合計: 7
- CRITICAL: 3(修正必須 — ブロックが正しくレンダリングされません)
- WARNING: 3(品質/互換性の問題)
- INFO: 1(ベストプラクティスの改善)
ブロック検証のリスク: HIGH — useBlockProps.save() がないと、検証エラーが発生します
セキュリティに関する注意: ABSPATH チェックの不足が検出されました。包括的なセキュリティ分析を行うには、/wp-sec-review を実行してください。
プラグインに関する注意: ブロックの登録が検出されました。プラグインのアーキテクチャをレビューするには、/wp-plugin-review を実行してください。
## Common Mistakes (BLK-25)
Patterns that look like issues but are NOT problems:
| Pattern | Why It's NOT a Problem | Context |
|---------|------------------------|---------|
| **Dynamic block save() returning null** | Server-side PHP renders output. save() doesn't need to return markup. | Correct for render_callback or render file usage |
| **InnerBlocks.Content without useBlockProps wrapper** | Valid for dynamic blocks that only need to persist nested blocks. | Dynamic blocks can return bare InnerBlocks.Content in save() |
| **viewScript not needed** | Block has no frontend JavaScript behavior. | Editor-only blocks or static HTML blocks don't need viewScript |
| **render_callback without return** | Valid if callback echoes output (though return is preferred). | WordPress handles both echo and return patterns |
| **Missing style/editorStyle in block.json** | Block uses theme defaults or has no custom styling. | Not all blocks need custom styles |
| **apiVersion 2 blocks** | Still work in WordPress 6.x+, just missing iframe isolation features. | Upgrade recommended but not critical |
| **Block without supports** | Valid for simple blocks that don't need color/spacing/typography. | Intentional for minimal blocks |
| **save() function with className in wrapper** | apiVersion 2 auto-injects classes. apiVersion 3 requires useBlockProps. | apiVersion-dependent behavior, not a bug |
| **window.wp.* in browser console** | Expected for debugging. Only @wordpress/* imports in source code matter. | Console access fine, source imports should use packages |
| **Interactivity API not used** | Blocks with no frontend interaction don't need Interactivity API. | Editor-only or static blocks work without frontend JS |
| **No block.json deprecation for attribute additions** | Adding new optional attributes doesn't break existing blocks. | Only save() markup changes need deprecation |
| **InnerBlocks without template** | Free-form nesting is valid. Template is optional. | Intentional when users should choose any blocks |
## Version Compatibility Reference
Quick reference for WordPress version requirements:
| Feature | WordPress Version | Notes |
|---------|-------------------|-------|
| block.json metadata support | 5.8+ | Single source of truth for block registration |
| apiVersion 2 | 5.6+ | Automatic wrapper class injection in save() |
| apiVersion 3 | 6.3+ | Iframe editor isolation, manual wrapper handling required |
| InspectorControls group prop | 6.2+ | Settings/Appearance tabs in sidebar |
| Block Hooks API | 6.4+ | Auto-insertion, dynamic blocks only |
| Interactivity API | 6.5+ | data-wp-* directives, viewScriptModule |
| viewScriptModule field | 6.5+ | ES module support for Interactivity API |
| Block Bindings API | 6.5+ | Dynamic attribute connections |
| useEntityProp hook | 5.9+ | Replacement for deprecated source:'meta' |
## Deep-Dive References
For advanced block development patterns, load these companion reference documents:
| Task | Reference to Load |
|------|-------------------|
| block.json schema validation, field-by-field reference, attribute type/source/selector patterns | `references/block-json-guide.md` |
| React/JSX editor component patterns, useBlockProps, RichText, InspectorControls, BlockControls, useSelect/useDispatch | `references/editor-patterns.md` |
| Server-side rendering, render_callback vs render file, get_block_wrapper_attributes, output escaping, $attributes/$content/$block parameters | `references/dynamic-blocks-guide.md` |
| Interactivity API directives (data-wp-*), wp_interactivity_state(), store() patterns, frontend state management | `references/interactivity-api-guide.md` |
**Note:** Reference docs provide deep-dive content. This SKILL.md is self-sufficient for standard block reviews.
**Security crossover:** When encountering security-relevant patterns (unescaped render output, user input in Interactivity API state, missing ABSPATH checks), this skill provides brief reminders but defers to wp-security-review for comprehensive security analysis. For detailed security patterns, use `/wp-sec-review` command.
**Plugin crossover:** When encountering plugin-level patterns (register_block_type() on init hook, ABSPATH checks, plugin headers), this skill provides brief mentions but defers to wp-plugin-development for plugin architecture depth. For detailed plugin architecture review, use `/wp-plugin-review` command.