2026年5月31日
2026年5月31日
Gutenbergのカスタムブロックを追加する方法|create-block・block.json・edit/save
WordPressのブロックエディター(Gutenberg)には、自分で作ったブロックをプラグインとして追加できます。公式の雛形作成ツール @wordpress/create-block を使うと、ビルドの設定が済んだプラグインが一式できるので、そこから中身を書き換えるのが近道です。
この記事では、見出しのレベル(H2 と H3)をツールバーで切り替えられる「見出しカード」ブロックを作ります。手順は2026年10月時点の @wordpress/create-block と @wordpress/scripts 37.1.0 で、雛形の作成からビルドまで実行して確かめています。この雛形が作るプラグインは WordPress 6.8 以上で動きます。
必要なもの
- Node.js の LTS 版と npm(雛形の作成とビルドに使う。サーバーではなく手元のパソコンでかまわない)
- WordPress 6.8 以上の開発環境
- 必要に応じて WP-CLI(有効化と確認に使う)
手順1: 雛形を作る
WordPress の wp-content/plugins に移動して、雛形を作ります。--namespace には、ブロック名の前半に使う自分用の名前を指定します。
cd wp-content/plugins
npx @wordpress/create-block@latest my-custom-block --namespace=myplugin
cd my-custom-block
依存パッケージのインストールと最初のビルドまで自動で行われるので、数分かかります。できあがる主なファイルは次のとおりです。
my-custom-block/
├── my-custom-block.php プラグイン本体(ブロックの登録)
├── package.json
├── src/
│ └── my-custom-block/
│ ├── block.json ブロックの定義
│ ├── index.js registerBlockType を呼ぶ入口
│ ├── edit.js エディターでの表示と編集
│ ├── save.js 保存するHTML
│ ├── style.scss 表側とエディターの両方に効くCSS
│ ├── editor.scss エディターだけに効くCSS
│ └── view.js 表側で動かすJavaScript
└── build/ ビルドの結果
├── blocks-manifest.php
└── my-custom-block/
├── block.json
├── index.js
├── index.asset.php
└── ……
src の中は、ブロックごとのフォルダー(ここでは my-custom-block)に分かれています。1つのプラグインに複数のブロックを入れるときは、このフォルダーを増やします。今回は表側の JavaScript を使わないので、view.js は削除してかまいません(block.json の viewScript も消します)。
手順2: block.json を書く
src/my-custom-block/block.json を次のようにします。
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "myplugin/my-custom-block",
"version": "0.1.0",
"title": "見出しカード",
"category": "text",
"icon": "star-filled",
"description": "見出しレベルを切り替えられる見出しのブロック",
"supports": {
"html": false,
"color": {
"background": true,
"text": true
},
"typography": {
"fontSize": true
}
},
"attributes": {
"content": {
"type": "string",
"source": "html",
"selector": "h2,h3"
},
"level": {
"type": "number",
"default": 2
}
},
"textdomain": "my-custom-block",
"editorScript": "file:./index.js",
"editorStyle": "file:./index.css",
"style": "file:./style-index.css"
}
nameは名前空間/ブロック名の形で、小文字の英数字とハイフンだけを使います。スラッシュが無いと登録できませんsupportsのcolorとtypographyを書くと、色と文字サイズの設定がサイドバーに自動で出ます。値はuseBlockPropsを通して、ブロックのクラスやスタイルとして付きますattributesはブロックが持つ値です。contentはsource: "html"とselectorで、保存したHTMLの見出しの中身から読み取ります。levelのようにsourceを書かない属性は、ブロックのコメント(<!-- wp:myplugin/my-custom-block {"level":3} -->)に保存されますeditorScriptなどのfile:./index.jsは、build/my-custom-block/の中のファイルを指します。ビルドするとblock.jsonもbuildにコピーされるので、パスはbuildから見た位置で書きます
手順3: edit.js(エディターでの表示)を書く
src/my-custom-block/edit.js です。ツールバーのボタンで見出しのレベルを切り替え、RichText で文字を入力します。
import { __ } from '@wordpress/i18n';
import {
useBlockProps,
RichText,
BlockControls,
} from '@wordpress/block-editor';
import { ToolbarGroup, ToolbarButton } from '@wordpress/components';
import './editor.scss';
export default function Edit( { attributes, setAttributes } ) {
const { content, level } = attributes;
const blockProps = useBlockProps();
return (
<>
<BlockControls>
<ToolbarGroup>
<ToolbarButton
label={ __( '見出しレベルを切り替え', 'my-custom-block' ) }
onClick={ () =>
setAttributes( { level: level === 2 ? 3 : 2 } )
}
>
{ `H${ level }` }
</ToolbarButton>
</ToolbarGroup>
</BlockControls>
<div { ...blockProps }>
<RichText
tagName={ `h${ level }` }
value={ content }
onChange={ ( value ) => setAttributes( { content: value } ) }
placeholder={ __( '見出しを入力…', 'my-custom-block' ) }
/>
</div>
</>
);
}
useBlockProps()の戻り値は、ブロックの一番外側の要素に付けます。これが無いと、ブロックの選択や色の設定が正しく働きません- 値を変えるときは、
setAttributes()に変える属性だけを渡します
手順4: save.js(保存するHTML)を書く
src/my-custom-block/save.js です。ここで返したHTMLが投稿の本文に保存され、表側にそのまま表示されます。
import { useBlockProps, RichText } from '@wordpress/block-editor';
export default function save( { attributes } ) {
const { content, level } = attributes;
return (
<div { ...useBlockProps.save() }>
<RichText.Content tagName={ `h${ level }` } value={ content } />
</div>
);
}
save の中では、useBlockProps ではなく useBlockProps.save() を使います。src/my-custom-block/index.js は雛形のまま(registerBlockType( metadata.name, { edit: Edit, save } ))で変更不要です。
手順5: PHP でブロックを登録する
雛形の my-custom-block.php には、次の登録処理がすでに書かれています。
function myplugin_my_custom_block_block_init() {
wp_register_block_types_from_metadata_collection( __DIR__ . '/build', __DIR__ . '/build/blocks-manifest.php' );
}
add_action( 'init', 'myplugin_my_custom_block_block_init' );
wp_register_block_types_from_metadata_collection() は WordPress 6.8 で入った関数で、ビルドのときに作られる build/blocks-manifest.php を読み、build の中のブロックをまとめて登録します。6.7 以前にも対応させたい場合は、ブロックのフォルダーを指定して register_block_type() で1つずつ登録します。
add_action( 'init', function () {
register_block_type( __DIR__ . '/build/my-custom-block' );
} );
register_block_type() には block.json があるフォルダー(ここでは build/my-custom-block)を渡します。build だけを渡しても、その直下に block.json が無いので登録されません。また、同じブロックを2回登録すると、「既に登録されています」という趣旨の通知が出ます。登録処理は1か所だけに書きます。
手順6: ビルドして有効化する
npm run build
wp plugin activate my-custom-block
WP-CLI が無い場合は、「プラグイン」画面で有効化します。投稿の編集画面でブロックの追加ボタンを押し、「見出しカード」を検索すると表示されます。
開発中は npm run start を実行しておくと、ファイルを保存するたびに自動でビルドされます。ブラウザのエディターは再読み込みが必要です。
ブロックが登録されているかは、WP-CLI で確かめられます。
wp eval 'echo WP_Block_Type_Registry::get_instance()->is_registered( "myplugin/my-custom-block" ) ? "registered" : "not registered";'
本番サーバーには、my-custom-block.php と build フォルダーがあれば動きます。npm run plugin-zip を実行すると、必要なファイルだけを ZIP にまとめられます。node_modules はアップロードしません。
動的ブロック(PHPで表示する)にする
最新の投稿一覧のように、表示のたびに内容が変わるブロックは、HTMLを保存せずにPHPで出力します。雛形を作るときに --variant dynamic を付けると、その形で作られます。
npx @wordpress/create-block@latest my-dynamic-block --namespace=myplugin --variant dynamic
この場合、block.json に "render": "file:./render.php" が入り、save は使いません。render.php では、$attributes(属性)、$content(内側のブロックのHTML)、$block(ブロックのオブジェクト)が使えます。今回の見出しブロックをPHPで出力するなら、次のように書けます。
<?php
$level = ( isset( $attributes['level'] ) && 3 === (int) $attributes['level'] ) ? 3 : 2;
$text = isset( $attributes['content'] ) ? $attributes['content'] : '';
?>
<div <?php echo get_block_wrapper_attributes(); ?>>
<h<?php echo $level; ?>><?php echo wp_kses_post( $text ); ?></h<?php echo $level; ?>>
</div>
見出しのレベルは2か3だけに限り、文字は wp_kses_post() で許可されたタグ以外を取り除いてから出力します。動的ブロックでは、content 属性に source を書かずに、ブロックのコメントに保存する形にします。
save を変えたときの「ブロック検証エラー」
save が返すHTMLを変えると、変更前に保存した投稿を開いたとき、「このブロックには、想定されていないか無効なコンテンツが含まれています」と表示されます。保存済みのHTMLと、今の save が作るHTMLが一致しないためです。
既存の投稿があるブロックの save を変えるときは、古い save を deprecated に残します。たとえば、前の版が div で囲まずに見出しだけを保存していた場合は、index.js を次のようにします。
import { registerBlockType } from '@wordpress/blocks';
import { useBlockProps, RichText } from '@wordpress/block-editor';
import './style.scss';
import Edit from './edit';
import save from './save';
import metadata from './block.json';
const v1 = {
attributes: metadata.attributes,
save( { attributes } ) {
const { content, level } = attributes;
return (
<RichText.Content
{ ...useBlockProps.save() }
tagName={ `h${ level }` }
value={ content }
/>
);
},
};
registerBlockType( metadata.name, {
edit: Edit,
save,
deprecated: [ v1 ],
} );
エディターは保存済みのHTMLを deprecated の save とも照らし合わせ、一致したものから属性を読み取ります。投稿を開いて保存し直すと、新しい形で保存されます。edit だけの変更は保存されるHTMLに影響しないので、自由に変えられます。
うまくいかないとき
| 症状 | 確認すること |
|---|---|
| ブロックの一覧に出ない | build フォルダーがあるか(npm run build を実行したか)。ブラウザの開発者ツールのコンソールにエラーが出ていないか |
| プラグインを有効化できない | 雛形のヘッダーに Requires at least: 6.8 があるため、6.7 以前では有効化できない。WordPress を更新するか、ヘッダーを書き換えて登録を register_block_type() に変える |
| 有効化すると致命的なエラーになる | エラーの内容を デバッグモード を有効にしてログで見る |
| 「想定されていないか無効なコンテンツ」と出る | save の出力を変えていないか。変えたなら deprecated を足す |
| CSS が効かない | style.scss の変更後にビルドしたか。block.json の style と editorStyle のパスが build の中のファイル名と合っているか |
npm run build が失敗する |
表示されたエラーのファイル名と行を確認する。詳しくは 自作ブロックがビルドエラーで使えなくなるときの原因と修正方法 を参照 |
@wordpress/blocks や @wordpress/components などのパッケージは、ビルドのときに WordPress 本体の wp.blocks などに置き換えられます。そのため、ブラウザで読み込まれるのは本体のもので、build/my-custom-block/index.asset.php に依存関係として書き出されます。
まとめ
npx @wordpress/create-block@latest ブロック名 --namespace=名前空間で雛形を作る- ブロックの名前・属性・使える設定は
block.jsonに書く edit.jsはuseBlockProps()とRichText、save.jsはuseBlockProps.save()とRichText.Contentで書く- 登録は雛形の
wp_register_block_types_from_metadata_collection()(WordPress 6.8 以上)か、register_block_type()にブロックのフォルダーを渡す - 表示のたびに内容が変わるブロックは
--variant dynamicで作り、render.phpで出力する saveを変えるときは、古い版をdeprecatedに残す
関連記事: