2026年5月19日
2026年5月19日
WordPressのGutenbergカスタムブロックをJavaScriptで開発する方法
はじめに
Gutenbergエディターでオリジナルのカスタムブロックを作成することで、コンテンツ編集者が使いやすいUIでオリジナルコンテンツを管理できるようになります。プラグインとして開発することで再利用性も高まります。
症状・原因
カスタムブロックが必要になる主なケース:
- デザイナーが作ったレイアウトをブロックとして提供したい
- ショートコードで実現していた機能をブロックに移行したい
- 商品カード・CTAボタンなど繰り返し使うUIを統一したい
- ACFフィールドとGutenbergを組み合わせたい
解決手順
ステップ1:開発環境をセットアップする
# Node.jsが必要(v16以上推奨)
node -v
# プラグインディレクトリを作成
mkdir wp-content/plugins/my-custom-blocks
cd wp-content/plugins/my-custom-blocks
# @wordpress/scripts でビルド環境をセットアップ
npm init -y
npm install --save-dev @wordpress/scripts
# package.json のscriptsを追加
# "scripts": {
# "build": "wp-scripts build",
# "start": "wp-scripts start",
# "lint:js": "wp-scripts lint-js"
# }
プラグインのPHPファイルを作成します:
<?php
/**
* Plugin Name: My Custom Blocks
* Description: カスタムGutenbergブロック集
* Version: 1.0.0
*/
// ブロックを登録
add_action('init', 'register_my_custom_blocks');
function register_my_custom_blocks() {
// block.jsonがある場合はこれだけでOK
register_block_type(__DIR__ . '/build/card-block');
}
ステップ2:block.jsonでブロックを定義する
src/card-block/block.jsonを作成します:
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "my-blocks/card",
"version": "1.0.0",
"title": "カードブロック",
"category": "design",
"icon": "format-image",
"description": "画像・タイトル・テキストのカードレイアウト",
"keywords": ["card", "カード", "レイアウト"],
"attributes": {
"title": {
"type": "string",
"default": ""
},
"content": {
"type": "string",
"default": ""
},
"imageUrl": {
"type": "string",
"default": ""
},
"imageAlt": {
"type": "string",
"default": ""
},
"buttonText": {
"type": "string",
"default": "詳しく見る"
},
"buttonUrl": {
"type": "string",
"default": ""
},
"backgroundColor": {
"type": "string",
"default": "#ffffff"
}
},
"supports": {
"html": false,
"align": ["wide", "full"]
},
"editorScript": "file:./index.js",
"editorStyle": "file:./index.css",
"style": "file:./style-index.css",
"viewScript": "file:./view.js"
}
ステップ3:edit関数でエディター内UIを実装する
src/card-block/index.jsを作成します:
import { registerBlockType } from '@wordpress/blocks';
import {
useBlockProps,
RichText,
MediaUpload,
MediaUploadCheck,
InspectorControls,
PanelColorSettings,
} from '@wordpress/block-editor';
import { Button, PanelBody, TextControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import metadata from './block.json';
registerBlockType(metadata.name, {
edit: function Edit({ attributes, setAttributes }) {
const { title, content, imageUrl, imageAlt, buttonText, buttonUrl, backgroundColor } = attributes;
const blockProps = useBlockProps({
style: { backgroundColor },
});
return (
<>
{/* サイドバーの設定パネル */}
<InspectorControls>
<PanelBody title="カード設定" initialOpen={true}>
<TextControl
label="ボタンURL"
value={buttonUrl}
onChange={(val) => setAttributes({ buttonUrl: val })}
placeholder="https://example.com"
/>
</PanelBody>
<PanelColorSettings
title="カラー設定"
colorSettings={[{
value: backgroundColor,
onChange: (color) => setAttributes({ backgroundColor: color }),
label: '背景色',
}]}
/>
</InspectorControls>
{/* エディター内プレビュー */}
<div {...blockProps} className="my-card-block">
<MediaUploadCheck>
<MediaUpload
onSelect={(media) => setAttributes({
imageUrl: media.url,
imageAlt: media.alt,
})}
allowedTypes={['image']}
render={({ open }) => (
<div className="my-card-image-wrap">
{imageUrl ? (
<img
src={imageUrl}
alt={imageAlt}
onClick={open}
style={{ cursor: 'pointer', width: '100%' }}
/>
) : (
<Button
onClick={open}
variant="secondary"
icon="upload"
>
{__('画像を選択')}
</Button>
)}
</div>
)}
/>
</MediaUploadCheck>
<div className="my-card-body">
<RichText
tagName="h3"
value={title}
onChange={(val) => setAttributes({ title: val })}
placeholder="タイトルを入力..."
allowedFormats={['core/bold', 'core/italic']}
/>
<RichText
tagName="p"
value={content}
onChange={(val) => setAttributes({ content: val })}
placeholder="本文を入力..."
/>
<RichText
tagName="span"
className="my-card-button"
value={buttonText}
onChange={(val) => setAttributes({ buttonText: val })}
/>
</div>
</div>
</>
);
},
save: function Save({ attributes }) {
const { title, content, imageUrl, imageAlt, buttonText, buttonUrl, backgroundColor } = attributes;
const blockProps = useBlockProps.save({
style: { backgroundColor },
});
return (
<div {...blockProps} className="my-card-block">
{imageUrl && (
<div className="my-card-image-wrap">
<img src={imageUrl} alt={imageAlt} />
</div>
)}
<div className="my-card-body">
<RichText.Content tagName="h3" value={title} />
<RichText.Content tagName="p" value={content} />
{buttonUrl && (
<a href={buttonUrl} className="my-card-button">
<RichText.Content tagName="span" value={buttonText} />
</a>
)}
</div>
</div>
);
},
});
ステップ4:スタイルを追加する
src/card-block/style.scss(フロントエンド + エディター共通):
.my-card-block {
border: 1px solid #e0e0e0;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
.my-card-image-wrap img {
width: 100%;
height: 200px;
object-fit: cover;
display: block;
}
.my-card-body {
padding: 1.5rem;
h3 {
margin: 0 0 0.75rem;
font-size: 1.25rem;
color: #1d2327;
}
p {
color: #666;
line-height: 1.6;
margin-bottom: 1rem;
}
}
.my-card-button {
display: inline-block;
background: #0073aa;
color: #fff;
padding: 0.5rem 1.25rem;
border-radius: 4px;
text-decoration: none;
font-weight: bold;
&:hover {
background: #005d8c;
}
}
}
ステップ5:ビルドして有効化する
# 開発ビルド(ウォッチモード)
npm run start
# 本番ビルド
npm run build
# ビルド後の構成
# build/
# ├── card-block/
# │ ├── index.js
# │ ├── index.css
# │ ├── style-index.css
# │ └── block.json
# WP-CLIでプラグインを有効化
wp plugin activate my-custom-blocks
# ブロックが登録されているか確認
wp block list | grep my-blocks
注意事項
save関数を変更すると、既存のブロックコンテンツがバリデーションエラーになります。変更時はdeprecationsを使って後方互換性を保ってください- React/JSXを使用するため
@wordpress/scriptsのビルド環境が必要です - PHPサーバーサイドレンダリングが必要なブロックは
render_callbackを使用してください
まとめ
Gutenbergカスタムブロック開発は、①@wordpress/scripts環境構築、②block.jsonでのメタ定義、③edit関数でのエディターUI、④save関数でのフロントエンド出力、⑤ビルドと有効化の流れで実装します。RichText・MediaUpload・InspectorControlsを組み合わせることで、直感的な編集UIを作れます。