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 に残す

関連記事:

お気軽にご相談ください

お見積りへ お問い合わせへ
▲