2026年10月13日
2026年10月13日
WordPressのブロックエディターにサイドバーパネルを追加する方法
はじめに
「ブロックエディターの右サイドバーに独自の設定パネルを追加したい」「投稿メタデータをエディターから管理できるUIを作りたい」「ブロックエディターにカスタムプラグインUIを統合したい」——@wordpress/pluginsと@wordpress/edit-postを使うとGutenbergのUIを拡張できます。
症状・原因
カスタムメタデータをクラシックエディターのメタボックスで管理していたが、ブロックエディターに対応した洗練されたUIが必要になった場合に実装します。PluginDocumentSettingPanelは投稿設定の「ステータス & 公開状態」パネルの下に追加され、PluginSidebarは独立したサイドバーとして追加されます。
解決手順
ステップ1:プロジェクトのセットアップ
# プラグインのビルド環境を構築
mkdir my-editor-plugin && cd my-editor-plugin
npm init -y
npm install --save-dev @wordpress/scripts
# package.json のスクリプトを設定
# "scripts": {
# "start": "wp-scripts start",
# "build": "wp-scripts build"
# }
npm run start
// my-editor-plugin.php: プラグインのメインファイル
<?php
/**
* Plugin Name: My Editor Plugin
* Description: ブロックエディターにカスタムパネルを追加
* Version: 1.0.0
*/
add_action( 'enqueue_block_editor_assets', function(): void {
$asset = require plugin_dir_path( __FILE__ ) . 'build/index.asset.php';
wp_enqueue_script(
'my-editor-plugin',
plugins_url( 'build/index.js', __FILE__ ),
$asset['dependencies'],
$asset['version']
);
} );
// カスタムメタフィールドをREST APIに公開
add_action( 'init', function(): void {
register_post_meta( 'post', '_article_note', [
'show_in_rest' => true,
'single' => true,
'type' => 'string',
'auth_callback' => fn() => current_user_can( 'edit_posts' ),
] );
register_post_meta( 'post', '_is_featured', [
'show_in_rest' => true,
'single' => true,
'type' => 'boolean',
'auth_callback' => fn() => current_user_can( 'edit_posts' ),
] );
register_post_meta( 'post', '_reading_time', [
'show_in_rest' => true,
'single' => true,
'type' => 'integer',
'auth_callback' => fn() => current_user_can( 'edit_posts' ),
] );
} );
ステップ2:PluginDocumentSettingPanelで投稿設定パネルを追加する
// src/index.js
import { registerPlugin } from '@wordpress/plugins';
import { PluginDocumentSettingPanel } from '@wordpress/editor';
import { useSelect, useDispatch } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';
import { store as editorStore } from '@wordpress/editor';
import {
TextareaControl,
ToggleControl,
TextControl,
PanelRow,
} from '@wordpress/components';
import { __ } from '@wordpress/i18n';
function MyDocumentPanel() {
const { editPost } = useDispatch( editorStore );
const { note, isFeatured, readingTime, postType } = useSelect( ( select ) => {
const { getEditedPostAttribute, getCurrentPostType } = select( editorStore );
const meta = getEditedPostAttribute( 'meta' ) || {};
return {
note: meta._article_note || '',
isFeatured: meta._is_featured || false,
readingTime: meta._reading_time || 0,
postType: getCurrentPostType(),
};
}, [] );
// post 投稿タイプのみ表示
if ( postType !== 'post' ) {
return null;
}
const updateMeta = ( key, value ) => {
editPost( { meta: { [ key ]: value } } );
};
return (
<PluginDocumentSettingPanel
name="my-document-panel"
title={ __( '記事の詳細設定', 'my-editor-plugin' ) }
icon="admin-settings"
>
<PanelRow>
<ToggleControl
label={ __( '注目記事', 'my-editor-plugin' ) }
help={ isFeatured
? __( 'トップページに表示されます', 'my-editor-plugin' )
: __( '通常表示', 'my-editor-plugin' )
}
checked={ isFeatured }
onChange={ ( val ) => updateMeta( '_is_featured', val ) }
/>
</PanelRow>
<PanelRow>
<TextControl
label={ __( '読了時間(分)', 'my-editor-plugin' ) }
type="number"
value={ readingTime }
min={ 1 }
onChange={ ( val ) => updateMeta( '_reading_time', parseInt( val, 10 ) ) }
style={ { width: '80px' } }
/>
</PanelRow>
<PanelRow>
<TextareaControl
label={ __( '編集メモ(公開されません)', 'my-editor-plugin' ) }
value={ note }
onChange={ ( val ) => updateMeta( '_article_note', val ) }
rows={ 4 }
/>
</PanelRow>
</PluginDocumentSettingPanel>
);
}
registerPlugin( 'my-document-panel', {
render: MyDocumentPanel,
icon: 'admin-settings',
} );
ステップ3:PluginSidebarで独立したサイドバーを追加する
// src/sidebar.js: 独立したサイドバーパネル
import { registerPlugin } from '@wordpress/plugins';
import { PluginSidebar, PluginSidebarMoreMenuItem } from '@wordpress/editor';
import { useSelect } from '@wordpress/data';
import { store as editorStore } from '@wordpress/editor';
import { PanelBody, PanelRow, Button, Notice } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
function MySidebar() {
const { wordCount, characterCount, postTitle } = useSelect( ( select ) => {
const { getEditedPostAttribute, getEditedPostContent } = select( editorStore );
const content = getEditedPostContent() || '';
const text = content.replace( /<[^>]+>/g, '' ); // タグ除去
return {
wordCount: text.split( /\s+/ ).filter( Boolean ).length,
characterCount: text.length,
postTitle: getEditedPostAttribute( 'title' ) || '',
};
}, [] );
const titleLength = postTitle.length;
const titleColor = titleLength > 60 ? '#d63638' : titleLength > 40 ? '#f0c30f' : '#00a32a';
return (
<>
<PluginSidebarMoreMenuItem target="my-sidebar" icon="chart-bar">
{ __( 'SEO・統計情報', 'my-editor-plugin' ) }
</PluginSidebarMoreMenuItem>
<PluginSidebar
name="my-sidebar"
title={ __( 'SEO・統計情報', 'my-editor-plugin' ) }
icon="chart-bar"
>
<PanelBody title={ __( 'タイトル分析', 'my-editor-plugin' ) } initialOpen={ true }>
<PanelRow>
<span>{ __( 'タイトル文字数:', 'my-editor-plugin' ) }</span>
<strong style={ { color: titleColor } }>{ titleLength }文字</strong>
</PanelRow>
{ titleLength > 60 && (
<Notice status="warning" isDismissible={ false }>
{ __( 'タイトルが60文字を超えています。SEOのため60文字以内を推奨します。', 'my-editor-plugin' ) }
</Notice>
) }
</PanelBody>
<PanelBody title={ __( '本文統計', 'my-editor-plugin' ) } initialOpen={ true }>
<PanelRow>
<span>{ __( '文字数:', 'my-editor-plugin' ) }</span>
<strong>{ characterCount.toLocaleString() }文字</strong>
</PanelRow>
<PanelRow>
<span>{ __( '単語数:', 'my-editor-plugin' ) }</span>
<strong>{ wordCount.toLocaleString() }語</strong>
</PanelRow>
<PanelRow>
<span>{ __( '推定読了時間:', 'my-editor-plugin' ) }</span>
<strong>{ Math.max( 1, Math.round( characterCount / 400 ) ) }分</strong>
</PanelRow>
</PanelBody>
</PluginSidebar>
</>
);
}
registerPlugin( 'my-sidebar', {
render: MySidebar,
} );
ステップ4:既存のメタボックスをAPIで非表示にする
// functions.php: 既存のメタボックスをブロックエディターで非表示
add_action( 'add_meta_boxes', function(): void {
// カスタムフィールドメタボックスを非表示(REST APIで管理するため)
remove_meta_box( 'postcustom', 'post', 'normal' );
}, 99 );
// ブロックエディターのデフォルトパネルを非表示
add_filter( 'default_hidden_meta_boxes', function( array $hidden ): array {
$hidden[] = 'tagsdiv-post_tag'; // タグパネルを初期非表示に
return $hidden;
}, 10 );
ステップ5:ビルドとWordPressへの組み込み
# 本番用ビルド
npm run build
# ディレクトリ構成
my-editor-plugin/
├── build/
│ ├── index.js # バンドルされたJS
│ └── index.asset.php # 依存関係(自動生成)
├── src/
│ └── index.js
└── my-editor-plugin.php
注意事項
register_post_meta()にshow_in_rest: trueを設定しないと、JavaScriptからuseSelectでメタデータを読み取れません。PluginDocumentSettingPanelは@wordpress/editorから、PluginSidebarも@wordpress/editorからインポートします(旧バージョンでは@wordpress/edit-postでしたが非推奨になりました)。useDispatch(editorStore).editPost()で変更したメタはUIに即反映されますが、実際の保存は投稿の「更新」ボタンを押したときです。
まとめ
Gutenbergサイドバーパネルの実装は「register_post_meta()でshow_in_rest:trueのメタを登録→enqueue_block_editor_assetsでJSを読み込み→PluginDocumentSettingPanelで投稿設定パネルを追加→useSelectでメタ取得・useDispatch(editorStore).editPost()でメタ更新→PluginSidebarで独立サイドバーを追加→npm run buildでビルド」の流れで整備します。関連記事:WordPressにカスタムブロックを追加する方法、WordPressのブロックパターンを作成する方法。