多くの開発者が、Gutenbergのブロックやアプリ開発を始めるハードルの高さに不満を抱いています。習得の難易度が高い理由は、開発環境のインストールと設定の複雑さにあります。しかも、ブロック開発には、JavaScript、Node.js、React、Reduxの深い知識が必要です。

WordPress公式のブロックエディターハンドブックは有用なリソースですが、開発者向けの膨大な情報の中で、初心者が迷子になることはほぼ間違いないでしょう。

Gutenbergプロジェクトのリード開発者Matías Ventura氏は、WP Tavernのインタビューで以下のように語っています。

すぐにブロック開発を習得できる人もいますが、やはり多くの人にとって大きな壁となっています。段階的な習得が必要になりますが、ドキュメントは、構成も表現も桁違いに改善できるはずです。もっといろいろなことができればと思っています。(英語原文の日本語訳)

そこで、今回の記事では、Gutenbergブロック開発について、徹底的に解説していきます。

それでは、早速始めましょう!

Gutenbergブロック開発の前提条件

WordPressプラグイン開発の知識と、基本的なHTML、CSS、JavaScript、Reactの知識のみを使って実行できる範囲でご説明していきます。

今回の記事は、やや長編になりそうです。

必要な情報をすべて盛り込みつつ、わかりやすい簡潔な解説を行うため、適切な妥協点を探り、取り上げるトピックを厳選しました。

中級者・上級者の方々は、ReactのステートReduxのストア高階コンポーネントなどに触れないことに不満を感じられるかもしれませんが、今回はご了承ください。これらのトピックを網羅すると、さらに長尺になり、(React開発者でない限り)ブロック開発の初心者にとって高度過ぎる内容になってしまうという判断です。

同じ理由で、ダイナミックブロックメタボックスなど、Gutenbergのブロック開発に関連する高度なトピックも今回は取り上げません。

しかし、この記事を通してブロック開発の基礎を理解すれば、すぐに生産的な開発を始められるはず。

そして、一度ブロック開発を始めれば、スキルを磨きながら、より高度なGutenbergブロックを作成できるようになるはずです。

Gutenbergのブロック

2018年12月のリリース以来、ブロックエディターは、あらゆる面で大きく改善されました。強力なAPI、高度なユーザーインターフェース、ユーザビリティの向上、数々のブロック、フルサイト編集の最初の実装など。

未だ開発中のGutenbergも、それなりに長い道のりを歩んできました。今日、ブロックエディターは、ページビルダーやサイトビルダーとして、信頼性も向上し、機能的で、十分な成長を見せています。

開発者の視点で見ると、Gutenbergは、WordPress利用者がコンテンツを作成、編集、削除できる、Reactベースのシングルページアプリケーション(SPA)です。ただし、従来のコンテンツエディターの拡張ではありません。

この点について、少し掘り下げてみましょう。

Gutenbergでは、コンテンツをブロックに分割します。ブロックは、投稿や固定ページ、あるいはウェブサイト全体の作成に使用できるレンガのようなものです。

では、技術的に見たブロックの定義とは何でしょうか。

WordPressがこれをうまく表現しています。

「ブロック」とは、ウェブページのコンテンツやレイアウトを構成するマークアップのまとまりを表す抽象的な用語である。アイデアとしては、今日のWordPressで、ショートコード、カスタムHTML、埋め込みの発見で実現している概念を、単一の一貫したAPIやユーザー体験と統合したものと言える。(英語原文の日本語訳)

タイトル、段落、カラム、画像、ギャラリー、そしてサイドバーパネルからブロックツールバーコントロールまで、エディターのインターフェースを構成するすべての要素は、Reactコンポーネントです。

では、Reactコンポーネントとは何でしょうか。W3Schoolsは、以下のように定義しています。

コンポーネントは、独立した、再利用可能な小さなコードである。JavaScriptの関数と同じ目的を果たすが、分離して動作し、render()関数でHTMLを返す。(英語原文の日本語訳)

WordPress 5.8のGutenbergブロック
WordPress 5.8のGutenbergブロック

Gutenbergは、従来のWordPressクラシックエディターとは異なりますが、データベース内でのコンテンツの保存方法は同じです。これは、GutenbergがWordPress内で動作するアプリケーションであり、コアでのCMSの動作に変化はないためです。

Gutenbergで作成した投稿(および固定ページ、カスタム投稿タイプ)は、クラシックエディター同様、wp_postsテーブルに格納されます。

しかし、テーブル内のGutenbergで作成された投稿には、クラシックエディターで作成された投稿と根本的に異なる情報が追加されています。

HTMLコメントのようにも見えるこの情報には、区切りブロックという機能があります。

コードエディターで表示したブログの投稿
コードエディターで表示したブログの投稿

このブロックを区切る、区切りブロックは、画面にレンダリングするブロックとJSONオブジェクトのブロックプロパティの値を定義します。プロパティは、ブロックが画面上でどのように表示されるかを決定します。

wp_postsテーブルに保存されたブログの投稿
wp_postsテーブルに保存されたブログの投稿

WordPress開発環境のセットアップ

最新のJavaScript開発環境をセットアップするには、WebpackReactJSXBabelESLintなどの高度な技術に関する確実な知識が求められます。

一見尻込みしてしまいそうですが、ご安心を。WordPressコミュニティが、すでに救いの手を差し伸べており、面倒な個別の設定を回避できる、優れたツールが存在しています。

この記事では簡潔な解説を心がけるため、トランスパイルについては触れません(ブロック開発の基本を習得後に学習することをお勧めします)。代わりに、最新のJavaScript開発環境を、素早く簡単に、数分でセットアップできる、2つの代替ツールを紹介します。開発するプロジェクトに応じて、便利な方を選択してください。

Gutenbergブロック構築用のJavaScript開発環境のセットアップは、以下の3つのステップで行います。

  1. Node.jsとnpmのインストール
  2. 開発環境のセットアップ
  3. ブロックプラグインのセットアップ

では、それぞれの手順を見ていきましょう。

1. Node.jsとnpmのインストール

開発環境をインストールし、最初のブロックを登録する前に、Node.jsとNodeパッケージマネージャ(npm)をインストールする必要があります。

Node.jsとnpmは、複数の方法インストールできますが、まずは、コンピュータにインストール済みかどうかを確認してください。

ターミナルを起動し、次のコマンドを実行します。

node -v

結果が「command not found(コマンドが見つかりません)」であれば、Node.jsはインストールされていません。このままインストールを進めてください。

この記事では、最も簡単なインストール方法であるNodeインストーラを使った方法をご紹介します。使用しているオペレーティングシステムに対応するバージョンをダウンロードし、ウィザードを起動するだけでインストールできます。

Node.jsのダウンロードページ
Node.jsのダウンロードページ

Node.jsのインストールが完了したら、再度ターミナルでnode -vコマンドを実行します。また、npm -vコマンドを実行して、npmパッケージを利用できることも確認します。

これで、以下のツールを使えるようになりました。

  • npx:Node.jsパッケージランナー(ドキュメント参照)。インストールせずに、npmコマンドを実行する。
  • npm:Node.jsパッケージマネージャ(ドキュメント参照)。依存関係のインストールとスクリプトを実行する。

次に開発環境をインストールします。

2. 開発環境のセットアップ

ローカル環境に最新版のNode.jsとnpmをインストールしたら、次に、WordPressの開発環境が必要です。

これには、DevKinstaのようなローカル開発環境を使うか、WordPressの公式ツールを使うかのどちらかになります。それぞれの方法を見てみましょう。

方法1. DevKinstaを使用する

KinstaのローカルWordPress開発ツール「DevKinsta」を使用すれば、数クリックでローカルにWordPressをインストールできます。また、DevKinstaの他に、MAMPXAMPPなど、他のローカル開発ツールも選択できます。

DevKinstaでの新しいWordPressウェブサイトの作成
DevKinstaでの新しいWordPressウェブサイトの作成

方法2. wp-envを使用する

公式ツール「wp-env」もあります。wp-envは、コマンドラインから直接起動して、ローカルのWordPress開発環境を作成できます。Noah Alen氏は、wp-envを以下のように定義しています。

WordPressのローカル環境はコマンド1つで、簡単に構築できるようになりました。wp-envを使用すると、個別の設定をしなくても簡単にローカル環境を構築し、オプションの指定により、時間をかけずに素早くWordPressを起動できます。wp-env開発によって、開発者、デザイナー、管理者など、誰もがローカル環境を簡単に利用できることが目標です。(英語原文の日本語訳)

wp-envは、以下の2ステップでインストールできます。

ステップ1. DockerとNode.jsのインストールを確認する

インストール要件を満たすには、まずDockerとNode.jsの両方をコンピュータにインストールする必要があります。wp-envは、WordPressウェブサイトを実行するDockerインスタンスを作成します。コードに加えられた変更は、即座にWordPressのインスタンスに反映されます。

ステップ2. コマンドラインから@wordpress/envをインストールする

DockerとNode.jsがコンピュータ上で動作している状態で、次に、WordPress開発環境をインストールします。

wp-envは、グローバルにもローカルにもインストールできます。グローバルにインストールするには、pluginsディレクトリからコマンドを実行してください(以下の「重要」セクションで詳しくご説明します)。

npm install -g @wordpress/env

コマンドのオプションは以下のとおりです。

wp-envが正常にインストールされたことを確認するには、次のコマンドを実行します。

wp-env --version

wp-envの現在のバージョンが表示されるはずです。これで、プラグインのフォルダから次のコマンドを使用して、環境を起動できます。

wp-env start

WordPressの管理画面には、以下のアドレスでアクセスできます。

  • http://localhost:8888/wp-admin/

デフォルトの認証情報は以下のとおりです。

  • ユーザー名:admin
  • パスワード:password

ブロックプラグインのセットアップ

次に、開発のベースとなるスターターブロックプラグインが必要ですが、すべてのファイルやフォルダを含むプラグインの準備は不要です。開発ツールを実行するだけで、ブロック開発に必要なすべてのファイルや設定を準備することができます。

これを行うには、2つの方法があります。それぞれ詳しく見ていきましょう。

方法1. @wordpress/create-blockを使用する

@wordpress/create-blockは、Gutenbergブロックを作成する、構成の不要な公式ツールです。

Create Blockは、ブロックのWordPressプラグインのひな形を作成する、公式サポートツールです。構成は不要で、モダンなビルド環境を構築します。PHP、JS、CSSコード、その他、プロジェクトの開始に必要なすべてのファイルを生成します。

Create Blockはcreate-react-appから多大な影響を受けました。@gaearon、Facebookの開発者、そしてReactコミュニティに大きな賛辞を送ります。

ローカル環境を起動したら、npx @wordpress/create-blockコマンドを実行するだけで、スターターブロックをセットアップできます。このコマンドは、プラグインのひな形を作成し、新しいブロックの登録に必要な、すべてのファイルとフォルダを準備します。

テストを実行し、どのように動作するかを確認してみます。

コマンドラインツールから、/wp-content/plugins/ディレクトリに移動して、次のコマンドを実行してください。

npx @wordpress/create-block my-first-block

確認を求められたら、yを入力して次に進みます。

@wordpress/create-blockを使用したブロックの作成
@wordpress/create-blockを使用したブロックの作成

この処理には少し時間がかかります。完了すると、以下のようなメッセージが表示されます。

ブロックプラグインが作成された状態
ブロックプラグインが作成された状態

以上です!

次にWordPressの開発環境を起動し、WordPress管理画面の「プラグイン」画面を表示します。一覧にプラグイン「My First Block」が追加されているはずです。

ブロックプラグインが正しくインストールされている
ブロックプラグインが正しくインストールされている

必要に応じてプラグインを有効化し、新しいブログ投稿を作成します。次に、ブロックインサーターを「ウィジェット」セクションまでスクロールして、新しいブロックを選択します。

@wordpress/create-blockで作成したブロックの例
@wordpress/create-blockで作成したブロックの例

ここでターミナルに戻り、カレントディレクトリをmy-first-blockに変更します。

cd my-first-block

以下のコマンドを実行します。

npm start

これでプラグインを開発モードで実行できます。本番用のコードを作成するには、以下のコマンドを実行します。

npm run build

方法2. create-guten-blockを使用する

create-guten-blockは、Gutenbergブロックを構築する、サードパーティ製の開発ツールです。

create-guten-blockは、構成の不要な開発ツールキット(#0CJS)です。React、webpack、ES6/7/8/Next、ESLint、Babelなどを個別に設定しなくても、WordPress Gutenbergブロックを数分で開発できます。

公式のcreate-blockツールと同様に、create-guten-blockも、create-react-appをベースにしており、最初のブロックプラグインを簡単に生成できます。

このツールキットには、最新のWordPressプラグインの作成に必要な、以下を含むすべての機能が揃っています。

  • React、JSX、ES6構文に対応
  • バックグラウンドで実行される、webpack開発用、本番用のビルドプロセス
  • オブジェクトスプレッド演算子など、ES6を超える言語拡張
  • -webkitなどの接頭辞が不要な、CSSの自動接頭辞
  • 本番用に、JS、CSS、画像をソースマップ付きでバンドルするビルドスクリプト
  • cgb-scriptsのみへの依存による機能の更新

なお、以下の点に注意してください。

トレードオフとして、これらのツールは特定の方法で動作するように構成されています。もし、プロジェクトで高度なカスタマイズが必要であれば、境界を越えて自由にカスタマイズできますが、そのときも、構成を維持する必要があります。

WordPressのローカルサイトを手元に用意したら、コマンドラインツールを起動し、インストール先の/wp-content/pluginsフォルダに移動して、以下のコマンドを実行します。

npx create-guten-block my-first-block

プロジェクト構造の作成、依存関係のダウンロードには、1~2分ほどかかります。

create-guten-blockを使用したGutenbergブロックの作成
create-guten-blockを使用したGutenbergブロックの作成

処理が完了すると、以下の画面が表示されます。

create-guten-blockで正しくブロックが作成された状態
create-guten-blockで正しくブロックが作成された状態

以下のスクリーンショットは、Visual Studio Code内で稼働しているターミナルとプロジェクト構造の様子です。

Visual Sudio Codeで表示したブロックプラグイン
Visual Sudio Codeで表示したブロックプラグイン

次に、WordPressの管理画面に戻ります。「プラグイン」画面に「my-first-block」プラグインが表示されているはずです。

create-guten-blockで作成したプラグイン
create-guten-blockで作成したプラグイン

プラグインを有効化し、ターミナルに戻ります。カレントディレクトリを「my-first-block」に変更し、npm startを実行します。

cd my-first-block
npm start

以下のような応答があるはずです。

npm start
npm start

この場合も、プラグインは開発モードで実行されます。本番用のコードを作成するには、以下のコマンドを使用します。

npm run build

プラグインを有効化して、新規投稿または固定ページを作成します。ブロックインスペクタを検索して、Gutenbergブロックを選択します。

create-guten-blockで作成したブロック
create-guten-blockで作成したブロック

詳細な概要や、発生したエラーへの対応については、Ahmad Awais氏のドキュメントをご覧ください。

スターターブロックのひな形の探索

create-blockcreate-guten-blockのどちらの開発ツールを選択しても、ブロックプラグイン構築の出発点として利用可能なブロックのひな形を取得できます。

ブロックのひな形とは何でしょうか。

ブロックのひな形とは、WordPressからブロックとして認識されるために必要なディレクトリ構造を表す言葉です。通常、このディレクトリにはindex.phpindex.jsstyle.cssなどのファイルがあり、ファイルの内部にはregister_block_typeなどの呼び出しが含まれます。

ブロックエディターハンドブックでも使用されている公式のCreate Block開発ツールを使用します。ただし、create-guten-blockのようなサードパーティツールでも大きな違いはないはずです。

それでは、create-blockツールを見ていきましょう。

開発ツール「Create Block」とは

前述のように、Create Blockは、Gutenbergブロックを作成する公式のコマンドラインツールです。ターミナルで@wordpress/create-blockを実行すると、新しいブロックタイプの登録に必要な、PHP、JS、SCSSファイルとコードが生成されます。

npx @wordpress/create-block [options] [slug]
  • [slug](任意):ブロックのスラッグの割り当てとプラグインのインストールに使用される
  • [options](任意):利用可能なオプション

デフォルトでは、ESNextテンプレートが割り当てられます。すなわち、JSX構文が追加された次バージョンのJavaScriptが作成されます。

ブロック名を省略すると、コマンドは対話モードで実行され、ファイル生成前に複数の設定を編集することができます。

npx @wordpress/create-block
対話モードでのcreate-blockの実行
対話モードでのcreate-blockの実行

下のスクリーンショットは、公式のCreate Blockツールで作成された、ブロックプラグインのファイル構成です。

@wordpress/create-blockで作成されたブロックプラグインのファイルとフォルダ
@wordpress/create-blockで作成されたブロックプラグインのファイルとフォルダ

ブロックプラグインの主なファイルとフォルダを確認します。

プラグインファイル

メインのプラグインファイルでは、サーバーにブロックを登録します。

<?php
/**
 * Plugin Name:       Kinsta Academy Block
 * Plugin URI:        https://kinsta.com/
 * Description:       An example block for Kinsta Academy students
 * Requires at least: 5.9
 * Requires PHP:      7.0
 * Version:           0.1.0
 * Author:            Kinsta Students
 * License:           GPL-2.0-or-later
 * License URI:       https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain:       ka-example-block
 *
 * @package           ka-example-block
 */

/**
 * Registers the block using the metadata loaded from the `block.json` file.
 * Behind the scenes, it registers also all assets so they can be enqueued
 * through the block editor in the corresponding context.
 *
 * @see https://developer.wordpress.org/reference/functions/register_block_type/
 */
function ka_example_block_ka_example_block_block_init() {
	register_block_type( __DIR__ . '/build' );
}
add_action( 'init', 'ka_example_block_ka_example_block_block_init' );

register_block_type関数は、block.jsonファイルに格納されたメタデータを使用して、サーバーにブロックタイプを登録します。

この関数は2つのパラメータを取ります。

  • 名前空間を含むブロックタイプ名、またはjsonファイルがあるフォルダのパス、または完全なWP_Block_Typeオブジェクト
  • ブロックタイプの引数の配列

上のコードでは、ブロックタイプの引数に、マジック定数__DIR__」が指定されています。これは、プラグインのファイルと同じフォルダにblock.jsonファイルが存在することを意味します。

package.jsonファイル

package.jsonファイルは、プロジェクトのJavaScriptプロパティとスクリプトを定義します。ここには、プロジェクトの依存関係もインストールできます。

コードエディターでファイルを開き、内容を確認してください。

{
	"name": "ka-example-block",
	"version": "0.1.0",
	"description": "An example block for Kinsta Academy students",
	"author": "Kinsta Students",
	"license": "GPL-2.0-or-later",
	"homepage": "https://kinsta.com/",
	"main": "build/index.js",
	"scripts": {
		"build": "wp-scripts build",
		"format": "wp-scripts format",
		"lint:css": "wp-scripts lint-style",
		"lint:js": "wp-scripts lint-js",
		"packages-update": "wp-scripts packages-update",
		"plugin-zip": "wp-scripts plugin-zip",
		"start": "wp-scripts start"
	},
	"devDependencies": {
		"@wordpress/scripts": "^24.1.0"
	},
	"dependencies": {
		"classnames": "^2.3.2"
	}
}

scriptsプロパティは、コマンドを含む連想配列です。コマンドはnpm run [cmd]を使用して、パッケージのライフサイクルの様々なタイミングで実行されます。

この記事では、以下のコマンドを使用します。

  • npm run build:(圧縮された)本番用ビルドを作成
  • npm run start:(圧縮されていない)開発用ビルドを作成

dependenciesdevDependenciesは、パッケージ名とバージョンを対応付ける2つのオブジェクトです。dependenciesは本番用ビルドで、devDependencesはローカル開発でのみ必要です(詳細はこちら)。

デフォルトのdevDependenciesは、@wordpress/scriptsパッケージのみで、これは「WordPress開発専用の、再利用可能なスクリプト集」として定義されています。

block.jsonファイル

WordPress 5.8以降、正式なブロックタイプの登録には、block.jsonメタデータファイルを使用します。

block.jsonファイルには、パフォーマンスの向上や、WordPressプラグインディレクトリでの視認性の向上など、様々な利点があります。

パフォーマンスの観点から、テーマがアセットの遅延ロードをサポートするとき、block.jsonで登録されたブロックは、デフォルトでアセットのキュー処理が最適化されます。styleまたはscriptプロパティにリストされたフロントエンドCSSやJavaScriptアセットは、ブロックがページ上に存在するときのみキューに入れられ、結果、ページサイズが縮小します。

@wordpress/create-blockコマンドを実行すると、以下のblock.jsonファイルが生成されます。

{
	"$schema": "https://schemas.wp.org/trunk/block.json",
	"apiVersion": 2,
	"name": "ka-example-block/ka-example-block",
	"version": "0.1.0",
	"title": "Kinsta Academy Block",
	"category": "widgets",
	"icon": "superhero-alt",
	"description": "An example block for Kinsta Academy students",
	"supports": {
		"html": false
	},
	"textdomain": "ka-example-block",
	"editorScript": "file:./index.js",
	"editorStyle": "file:./index.css",
	"style": "file:./style-index.css"
}

以下は、デフォルトのプロパティの一覧です。

  • apiVersion:ブロックが利用するAPIのバージョン(現在のバージョンは2)
  • name:名前空間を含むブロックの一意な識別子
  • version:ブロックの現在のバージョン
  • title:ブロックの表示タイトル
  • category:ブロックのカテゴリ
  • iconDashiconのスラッグまたは自作SVGアイコン
  • description:ブロックインスペクタに表示される短い説明
  • supports:エディターで使用される機能を制御するオプション群
  • textdomain:プラグインのテキストドメイン
  • editorScript:エディタースクリプト定義
  • editorStyle:エディタースタイル定義
  • style:ブロックの代替スタイルを指定

上のプロパティに加えて属性オブジェクトを定義でき、ブロックが保存するデータに関する情報を指定できます。block.jsonでは、キーと値のペアでいくつでも属性を設定できます。この時、キーは属性名、値は属性定義です。

以下は属性定義の例です。

"attributes": {
	"content": {
		"type": "array",
		"source": "children",
		"selector": "p"
	},
	"align": {
		"type": "string",
		"default": "none"
	},
	"link": { 
		"type": "string", 
		"default": "https://kinsta.com" 
	}
},

block.jsonファイルについてはこの記事の後半で詳しくご説明しますが、ブロックエディターハンドブックでも、block.jsonファイルのメタデータ属性について詳しく解説されていますので、参照してみてください。

srcフォルダ

srcフォルダは、開発が行われる場所です。フォルダには、以下のファイルがあります。

  • index.js
  • edit.js
  • save.js
  • editor.scss
  • style.scss

index.js

index.jsファイルは出発点です。依存関係をインポートし、ブロックタイプをクライアントに登録します。

import { registerBlockType } from '@wordpress/blocks';

import './style.scss';

import Edit from './edit';
import save from './save';
import metadata from './block.json';

registerBlockType( metadata.name, {
	/**
	 * @see ./edit.js
	 */
	edit: Edit,

	/**
	 * @see ./save.js
	 */
	save,
} );

最初の文では、@wordpress/blocksパッケージからregisterBlockType関数をインポートしています。続くimport文は、スタイルシート、Edit関数、save関数をインポートしています。

registerBlockType関数は、クライアントでコンポーネントを登録します。この関数は2つのパラメータを取ります。ブロック名の「名前空間/ブロック名」(サーバー上で登録されたものと同じ)とブロック構成オブジェクトです。

Edit関数はブロックエディターで描画されるブロックインターフェースを定義し、save関数はシリアライズされてデータベースに保存される構造を定義します(詳しくはこちら)。

edit.js

edit.jsでは、ブロック管理インターフェースを構築します。

import { __ } from '@wordpress/i18n';
import { useBlockProps } from '@wordpress/block-editor';
import './editor.scss';

export default function Edit() {
	return (
		<p {...useBlockProps()}>
			{__('My First Block – hello from the editor!', 'my-first-block')}
		</p>
	);
}

まず、@wordpress/i18nパッケージ(このパッケージには、JavaScript版の翻訳関数が含まれています)の__関数、useBlockProps Reactフックeditor.scssファイルをインポートしています。

続いて、Reactコンポーネントをエクスポートします(詳細はimport文とexport文をご覧ください)。

save.js

save.jsファイルでは、データベースに保存するブロック構造を構築します。

import { __ } from '@wordpress/i18n';
import { useBlockProps } from '@wordpress/block-editor';

export default function save() {
	return (
		<p {...useBlockProps.save()}>
			{__(
				'My First Block – hello from the saved content!',
				'my-first-block'
			)}
		</p>
	);
}

editor.scssとstyle.scss

スクリプトとは別に、2つのSASSファイルがsrcフォルダに存在します。editor.scssファイルにはエディターのコンテキストでブロックに適用されるスタイルが含まれ、style.scssファイルにはフロントエンドとエディターでの表示用のブロックのスタイルが含まれます。これらのファイルについては、この記事の後半で掘り下げていきます。

node_modulesとbuildフォルダ

node_modulesフォルダには、nodeモジュールとその依存関係が格納されています。nodeパッケージは、この記事で扱う範囲を超えるため深入りしませんが、npmがパッケージをインストールする場所については、この記事をご覧ください。

buildフォルダには、ビルド処理で生成されたJSファイルとCSSファイルが格納されます。詳しいビルドプロセスについては、「ESNext構文」と「JavaScriptビルド環境のセットアップ」をご覧ください。

実践─Gutenbergブロックを作成しよう

それでは、実際にブロックを作っていきましょう。このセクションでは、CTA(コール・トゥ・アクション)ブロック、Kinsta Academyブロックを実装するプラグインの作成方法をご説明します。

ブロックは2つのカラムで構成され、左側に画像、右側にテキストの段落があります。テキストの下には、編集可能なリンクが付いたボタンが配置されます。

この記事で構築方法を学ぶブロックタイプ
この記事で構築方法を学ぶブロックタイプ

これは簡単な例ですが、Gutenbergブロック開発の基本を網羅することができます。一通り理解できたら、ブロックエディターハンドブックなどのリソースを参照して、より複雑なGutenbergブロックを作ってみてください。

今回は、ローカル開発環境で最新版のWordPressが稼働していることを前提とし、以下の順番でブロック開発をご紹介していきます。

では、始めましょう。

スターターブロックプラグインのセットアップ方法

コマンドラインツールを起動して、/wp-content/pluginsフォルダに移動します。

macOSの「フォルダに新規ターミナル」
macOSの「フォルダに新規ターミナル」

以下のコマンドを実行します。

npx @wordpress/create-block

このコマンドは対話モードで、ブロック登録用のPHP、SCSS、JSファイルを生成します。対話モードでは、ブロックに必要なデータを簡単に設定できます。この記事では、以下の値を使用します。

  • Template variant(テンプレート):static(静的)
  • Block slug(ブロックのスラッグ):ka-example-block
  • Internal namespace(内部名前空間):ka-example-block
  • Block display title(ブロック表示タイトル):Kinsta Academy Block
  • Short block description(ブロックの説明):An example block for Kinsta Academy students(Kinstaアカデミー受講者向けブロックの例)
  • Dashicon(アイコンフォント):superhero-alt
  • Category name(カテゴリ名):widgets
  • Do you want to customize the WordPress plugin?(WordPressプラグインを編集しますか?):Yes
  • The home page of the plugin(プラグインのトップページ):https://kinsta.com/
  • Current plugin version(現在のプラグインのバージョン):1.0
  • Plugin author(プラグイン開発者名) :自分の名前
  • License(ライセンス):─
  • Link to the license text(ライセンステキストへのリンク):─
  • Custom domain path for translation(翻訳用独自ドメインパス):─

プラグインとすべての依存関係をインストールするには、数分かかります。処理が完了すると、以下のようなメッセージが表示されます。

開発用ブロックのインストールと登録
開発用ブロックのインストールと登録

次に、/wp-content/pluginsフォルダから以下のコマンドを実行します。

cd ka-example-block

Visual Studio Codeターミナルからのコマンドの実行
Visual Studio Codeターミナルからのコマンドの実行

最後に、プラグインのフォルダ(この例ではka-example-block)から次のコマンドを入力して、開発を始めます。

npm start

「プラグイン」画面を開き、「Kinsta Academy Block」プラグインを有効化してください。

サンプルブロックを有効化
サンプルブロックを有効化

新しい投稿を作成し、ブロックインサーターを開き、「デザイン」カテゴリまでスクロールします。「Kinsta Academy Block」をクリックして、投稿に追加してください。

@wordpress/create-blockで作成したスターターブロック
@wordpress/create-blockで作成したスターターブロック

block.jsonの編集

前述したように、サーバーサイドのブロック登録はメインの.phpファイルで行われます。しかし、ここでは.phpファイル内で設定を定義しません。代わりに、block.jsonファイルを使用します。

もう一度block.jsonを開いて、デフォルトの設定を詳しく見てみましょう。

{
	"$schema": "https://schemas.wp.org/trunk/block.json",
	"apiVersion": 2,
	"name": "ka-example-block/ka-example-block",
	"version": "0.1.0",
	"title": "Kinsta Academy Block",
	"category": "widgets",
	"icon": "superhero-alt",
	"description": "An example block for Kinsta Academy students",
	"supports": {
		"html": false
	},
	"textdomain": "ka-example-block",
	"editorScript": "file:./index.js",
	"editorStyle": "file:./index.css",
	"style": "file:./style-index.css"
}

スクリプトとスタイル

editorScripteditorStylestyleプロパティには、フロントエンドとバックエンドのスクリプトとスタイルへの相対パスを指定します。

ここで定義したスクリプトとスタイルは、WordPressによって自動的に登録され、キューに入れられるため、手動で登録する必要はありません。確認のため、ブラウザの「検証」を起動し、「ネットワーク」タブを開いてください。

Chromeデベロッパーツールでのリソースの様子
Chromeデベロッパーツールでのリソースの様子

上のスクリーンショットからもわかるように、buildフォルダ内のindex.jsスクリプトは、正しくキューに入れられています。PHPコードを記述する必要はありません

UIラベル

titledescriptionプロパティには、エディター上でのブロックの識別に必要なラベルを指定します。

ブロックサイドバーのブロック名と説明
ブロックサイドバーのブロック名と説明

キーワード

前述したように、プロパティ属性を使用してブロックを設定できます。例えば、1つ以上のkeywordsを追加して、ブロックを検索しやすくできます。

"keywords": [ 
		"kinsta", 
		"academy", 
		"superhero" 
	],

クイックインサーターに「kinsta」「academy」または「superhero」を入力すると、エディターに「Kinsta Academy」ブロックが表示されます。

クイックインサーターのキーワードを使用したブロックの検索
クイックインサーターのキーワードを使用したブロックの検索

ローカライズ

JSONファイル内の文字列をローカライズする方法については、こちらをご覧ください。

JavaScript内では、@wordpress/blocksパッケージのregisterBlockTypeFromMetadataメソッドと、block.jsonファイルから読み込んだメタデータを使用して、ブロックタイプを登録できるようになりました。すべてのローカライズされたプロパティは、PHPのregister_block_type_from_metadataと同様に、自動的に_x@wordpress/i18nパッケージ)関数呼び出しにラップされます。唯一の要件として、block.jsonファイルにtextdomainプロパティを設定してください。

ここでは、registerBlockTypeFromMetadataの代わりにregisterBlockType関数を使用します。Gutenberg 10.7以降、registerBlockTypeFromMetadataが非推奨になったためですが、仕組みは同じです。

組み込みコンポーネントの使用─RichTextコンポーネント

Gutenbergブロックを構成する要素は、Reactコンポーネントであり、wpグローバル変数でアクセスできます。例えば、ブラウザのコンソールにwp.editorと入力すると、wp.editorモジュールに含まれるコンポーネントの一覧が表示されます。

リストをスクロールして、コンポーネントの名前から中身を推測してみてください。

同様に、wp.componentsモジュールに含まれるコンポーネントの一覧を確認できます。

WPコンポーネント
WPコンポーネント

edit.jsファイルに戻り、スクリプトを詳しく見てみましょう。

import { __ } from '@wordpress/i18n';
import { useBlockProps } from '@wordpress/block-editor';
import './editor.scss';

export default function Edit() {
	return (
		<p { ...useBlockProps() }>
			{ __(
				'Kinsta Academy Block – hello from the editor!',
				'ka-example-block'
			) }
		</p>
	);
}

このコードは、シンプルで編集できないテキストを含む静的ブロックを生成しますが、簡単に変更できます。

コードエディターで表示したスターターブロック
コードエディターで表示したスターターブロック

テキストを編集可能にするには、現在の<p>タグを、入力内容を編集できるコンポーネントで置き換えます。Gutenbergには、この目的で使用できる組み込みのRichTextコンポーネントがあります。

組み込みコンポーネントは、5つのステップでブロックに追加できます。

    1. WordPressパッケージから必要なコンポーネントをインポートする
    2. JSXコードに対応する要素を追加する
    3. block.jsonファイルに必要な属性を定義する
    4. イベントハンドラを定義する
    5. データを保存する

ステップ1. WordPressパッケージから必要なコンポーネントをインポートする

edit.jsファイルを開き、import文を変更します。

import { useBlockProps } from '@wordpress/block-editor';

上のimport文を以下のように変更してください。

import { useBlockProps, RichText } from '@wordpress/block-editor';

@wordpress/block-editorパッケージから、useBlockProps関数とRichTextコンポーネントをインポートしています。

useBlockProps

useBlockProps Reactフックは、ブロックのラッパー要素をマークします。

APIバージョン2を使用するには、ブロックのedit関数内で新しいuseBlockPropsフックを使用して、ブロックのラッパー要素をマークする必要があります。useBlockPropsフックは、ブロックの動作の有効化に必要な属性とイベントハンドラを挿入します。ブロック要素にはuseBlockPropsを介して属性を渡し、戻り値は要素に展開する必要があります。

つまり、useBlockPropsは、ラッパー要素(この例ではp要素)に自動的に属性とクラスを割り当てます。

 useBlockPropsで生成された要素とクラス
useBlockPropsで生成された要素とクラス

ラッパー要素からuseBlockPropsを削除すると、単純なテキスト文字列になってしまい、ブロックの機能やスタイルにアクセスできません。

useBlockPropsのないブロック
useBlockPropsのないブロック

後で触れますがuseBlockPropsには、プロパティのオブジェクトを渡しても、出力をカスタマイズできます。

RichText

RichTextコンポーネントには編集可能な入力領域があり、ユーザーはコンテンツを編集し、書式を設定できます。

GitHubのRichTextコンポーネントのドキュメントをご覧ください。

ステップ2. JSXコードに対応する要素を追加する

...

const blockProps = useBlockProps();

return (
	<RichText 
		{ ...blockProps }
		tagName="p"
		onChange={ onChangeContent }
		allowedFormats={ [ 'core/bold', 'core/italic' ] }
		value={ attributes.content }
		placeholder={ __( 'Write your text...' ) }
	/>
);

各行は以下の通りです。

  • tagName:編集可能なHTML要素のタグ名
  • onChange:要素の内容が変更されたときに呼び出される関数
  • allowedFormats:許可されるフォーマットの配列(デフォルトでは、すべてのフォーマットが許可されます)
  • value:編集可能になるHTML文字列
  • placeholder:要素が空のときに表示されるプレースホルダテキスト

ステップ3. block.jsonファイルに必要な属性を定義する

属性は、リッチコンテンツ、背景色、URLなど、ブロックが保存するデータに関する情報を保持します。

attributesオブジェクト内には、キーと値のペアで任意の数の属性を設定できます。キーは属性名、値は属性定義です。

block.jsonファイルを開き、以下のattributesプロパティを追加します。

"attributes": {
	"content": {
		"type": "string",
		"source": "html",
		"selector": "p"
	}
},

content属性は、編集可能フィールドに入力されたテキストを格納できます。

  • typeは、属性によって保存されるデータの種類を示します。このプロパティは、enumプロパティを定義しない限り必須です。
  • sourceは、投稿のコンテンツから、どのように属性値を抽出するかを定義します。この例では、HTMLコンテンツです。注意)このプロパティを指定しなければ、データはブロックデリミタに格納されます(詳細はこちら)。
  • selectorは、HTMLタグ、またはクラス名やid属性などのその他のセレクタです。

Edit関数には、プロパティのオブジェクトを渡します。edit.jsファイルに戻り、以下のように変更してください。

export default function Edit( { attributes, setAttributes } ) { ... }

ステップ4. イベントハンドラを定義する

RichText要素にはonChange属性があり、要素の内容が変更されたときに呼び出される関数を指定します。

以下は、この関数を定義したedit.jsスクリプトの全文です。

import { __ } from '@wordpress/i18n';
import { useBlockProps, RichText } from '@wordpress/block-editor';
import './editor.scss';

export default function Edit( { attributes, setAttributes } ) {
	const blockProps = useBlockProps();

	const onChangeContent = ( newContent ) => {
		setAttributes( { content: newContent } )
	}

	return (
		<RichText 
			{ ...blockProps }
			tagName="p"
			onChange={ onChangeContent }
			allowedFormats={ [ 'core/bold', 'core/italic' ] }
			value={ attributes.content }
			placeholder={ __( 'Write your text...' ) }
		/>
	);
}

ファイルを保存して、ターミナルウィンドウでnpm run startを実行します。WordPressの管理画面に戻り、新規投稿または固定ページを作成し、Kinsta Academyブロックを追加します。

ブロックエディターでのRichTextコンポーネントの出力
ブロックエディターでのRichTextコンポーネントの出力

テキストを入力して、コードビューに切り替えます。コードは以下のようになります。

<!-- wp:ka-example-block/ka-example-block -->
<p class="wp-block-ka-example-block-ka-example-block">Kinsta Academy Block – hello from the saved content!</p>
<!-- /wp:ka-example-block/ka-example-block -->

ここでページを保存してフロントエンドの表示をチェックしても、残念なことに変更はサイトに反映されていません。save.jsファイルを修正して、投稿を保存する際、データベースに入力を保存する必要があります。

ステップ5. データを保存する

save.jsファイルを開き、スクリプトを次のように変更します。

import { __ } from '@wordpress/i18n';
import { useBlockProps, RichText } from '@wordpress/block-editor';

export default function save( { attributes } ) {
	const blockProps = useBlockProps.save();
	return (
		<RichText.Content 
			{ ...blockProps } 
			tagName="p" 
			value={ attributes.content } 
		/>
	);
}

ここでは以下を実行しています。

  • block-editorパッケージからRichTextコンポーネントをインポートする
  • save関数にオブジェクト引数を介して複数のプロパティを渡す(この例ではattributesプロパティのみを渡しています)
  • RichTextコンポーネントのコンテンツを返す
ブロックの内容がデータベースに保存される
ブロックの内容がデータベースに保存される

RichTextコンポーネントの詳細については、ブロックエディターハンドブック、プロパティの一覧については、Githubを参照してください。

次は、ブロックツールバーにコントロールを追加します。

ブロックツールバーへのコントロールの追加

ブロックツールバーには、ブロックコンテンツの一部を操作できるコントロールが並びます。各ツールバーコントロールには、コンポーネントがあります。

コアの段落ブロックのツールバー
コアの段落ブロックのツールバー

例えば、ブロックにはテキスト配置コントロールを追加できます。必要な作業は、@wordpress/block-editorパッケージから2つのコンポーネントをインポートするだけです。

前の例と同じ手順で進めていきます。

  1. WordPressパッケージから必要なコンポーネントをインポートする
  2. JSXコードに対応する要素を追加する
  3. block.jsonファイルに必要な属性を定義する
  4. イベントハンドラを定義する
  5. データを保存する

ステップ1. @wordpress/block-editorからBlockControlsとAlignmentControlコンポーネントをインポートする

ブロックツールバーに配置コントロールを追加するには、2つのコンポーネントが必要です。

  • BlockControlsは、コントロールの動的ツールバーをレンダリングします(ドキュメントなし)。
  • AlignmentControlは、選択されたブロックの整列方法を表示するドロップダウンメニューをレンダリングします(詳細についてはこちら)。

edit.jsを開き、以下のようにimport文を編集します。

import { 
	useBlockProps, 
	RichText, 
	AlignmentControl, 
	BlockControls 
} from '@wordpress/block-editor';

ステップ2. BlockControlsとAlignmentControl要素を追加する

Edit関数に移動し、<BlockControls />要素を<RichText />と同じレベルで挿入します。次に、<BlockControls />の中に<AlignmentControl />を追加します。

export default function Edit( { attributes, setAttributes } ) {
	const blockProps = useBlockProps();
	return (
		<>
			<BlockControls>
				<AlignmentControl
					value={ attributes.align }
					onChange={ onChangeAlign }
				/>
			</BlockControls>
			<RichText 
				{ ...blockProps }
				tagName="p"
				onChange={ onChangeContent }
				allowedFormats={ [ 'core/bold', 'core/italic' ] }
				value={ attributes.content }
				placeholder={ __( 'Write your text...' ) }
				style={ { textAlign: attributes.align } }
			/>
		</>
	);
}

上のコードで<></>は、Reactで複数の要素を返す「フラグメント」を宣言する短い記法です。

この例では、AlignmentControlに2つの属性があります。

  • valueは、この要素の現在の値を表します
  • onChangeは、値が変更されたときに実行するイベントハンドラを指定します

また、RichText要素に追加の属性も定義しています(例付きの属性の一覧についてはこちら)。

ステップ3. block.jsonにalgin属性を定義する

block.jsonファイルに移動して、align属性を追加します。

"align": {
	"type": "string",
	"default": "none"
}

ブロックエディターに戻り、ページを更新してブロックを選択します。ブロックツールバーに、配置コントロールが表示されるはずです。

ブロックがエラーメッセージを表示
ブロックがエラーメッセージを表示

これは、まだイベントハンドラを定義していないのが原因です。

ステップ4. イベントハンドラを定義する

次に、onChangeAlignを以下のように定義します。

const onChangeAlign = ( newAlign ) => {
	setAttributes( { 
		align: newAlign === undefined ? 'none' : newAlign, 
	} )
}

newAlignundefinedであれば、noneに設定し、そうでなければ、newAlignを使用します。

これで、edit.jsスクリプトは(一旦)完了です。

export default function Edit( { attributes, setAttributes } ) {
	const blockProps = useBlockProps();
	const onChangeContent = ( newContent ) => {
		setAttributes( { content: newContent } )
	}
	const onChangeAlign = ( newAlign ) => {
		setAttributes( { 
			align: newAlign === undefined ? 'none' : newAlign, 
		} )
	}
	return (
		<>
			<BlockControls>
				<AlignmentControl
					value={ attributes.align }
					onChange={ onChangeAlign }
				/>
			</BlockControls>
			<RichText 
				{ ...blockProps }
				tagName="p"
				onChange={ onChangeContent }
				allowedFormats={ [ 'core/bold', 'core/italic' ] }
				value={ attributes.content }
				placeholder={ __( 'Write your text...' ) }
				style={ { textAlign: attributes.align } }
			/>
		</>
	);
}

これでエディターに戻って、ブロックのコンテンツを配置することができます。ブロックに配置ツールバーが表示されているはずです。

ブロックに配置ツールバーを追加
ブロックに配置ツールバーを追加

なお、投稿を保存すると、ブロックのコンテンツがブロックエディターで表示されるようにフロントエンドで配置されないことがあります。この場合、save関数を修正して、ブロックのコンテンツと属性をデータベースに保存する必要があります。

ステップ5. データを保存する

save.jsを開き、save関数を以下のように変更してください。

export default function save( { attributes } ) {
	const blockProps = useBlockProps.save();
	return (
		<RichText.Content 
			{ ...blockProps } 
			tagName="p" 
			value={ attributes.content } 
			style={ { textAlign: attributes.align } }
		/>
	);
}

最後に、分割代入構文を使用して、attributeオブジェクトから個々のプロパティを抽出し、コードを整理しましょう。

export default function save( { attributes } ) {
	const blockProps = useBlockProps.save();
	const { content, align } = attributes;
	return (
		<RichText.Content 
			{ ...blockProps } 
			tagName="p" 
			value={ content } 
			style={ { textAlign: align } }
		/>
	);
}

edit.jsファイルでも同じことができます。

ファイルを保存して、コードエディターモードでエディターに戻ります。コードは以下のようになるはずです。

<!-- wp:ka-example-block/ka-example-block {"align":"right"} -->
<p class="wp-block-ka-example-block-ka-example-block" style="text-align:right">This is my first editable <strong>Gutenberg</strong> <em>block</em> 😎</p>
<!-- /wp:ka-example-block/ka-example-block -->
ブロックツールバーコントロールの確認
ブロックツールバーコントロールの確認

これで完了です。ブロックツールバーに配置コントロールを追加できました🤓

ブロックツールバーコントロールの詳細については、ブロックエディターハンドブックの「ブロックツールバーと設定サイドバー」を参照してください。

ブロック設定サイドバーの編集

コントロールは、ブロック設定サイドバーにも追加できます(アプリケーション用に新規サイドバーを作成することも)。

APIとしては、InspectorControlsコンポーネントがあります。

ブロックエディターハンドブックで、設定サイドバーの使い方を以下のように説明しています。

設定サイドバーは、あまり使わない設定や、大きな画面スペースが必要な設定で使用されます。設定サイドバーはブロックレベル設定でのみ使用してください。

ブロック内の選択したコンテンツでのみ有効な設定は(例:段落内の選択したテキストに対する「太字」設定)、設定サイドバーの中に置かないでください。設定サイドバーはHTMLモードでブロックを編集するときにも表示されるため、ブロックレベルの設定のみを配置する必要があります。

再び、同じ手順で進めます。

  1. WordPressパッケージから必要なコンポーネントをインポートする
  2. JSXコードに対応する要素を追加する
  3. block.jsonファイルに必要な属性を定義する
  4. イベントハンドラを定義する
  5. データを保存する

ステップ1. @wordpress/block-editorからInspectorControlsとPanelColorSettingsコンポーネントをインポートする

ブロックの特定部分をカスタマイズできるようにするには、必要なコントロールを追加します。例えば、カラーコントロールパネルを実装するには、block-editorモジュールからInspectorControlsPanelColorSettingsコンポーネントをインポートします。

import { 
	useBlockProps, 
	RichText, 
	AlignmentControl, 
	BlockControls,
	InspectorControls,
	PanelColorSettings
} from '@wordpress/block-editor';

ステップ2. JSXコードに対応する要素を追加する

次に、Edit関数が返すJSXに、対応する要素を追加します。

export default function Edit( { attributes, setAttributes } ) {
	const blockProps = useBlockProps();

	const { content, align, backgroundColor, textColor } = attributes;

	const onChangeContent = ( newContent ) => {
		setAttributes( { content: newContent } )
	}
	const onChangeAlign = ( newAlign ) => {
		setAttributes( { 
			align: newAlign === undefined ? 'none' : newAlign, 
		} )
	}
	return (
		<>
			<InspectorControls>
				<PanelColorSettings 
					title={ __( 'Color settings', 'ka-example-block' ) }
					initialOpen={ false }
					colorSettings={ [
						{
						  value: textColor,
						  onChange: onChangeTextColor,
						  label: __( 'Text color', 'ka-example-block' )
						},
						{
						  value: backgroundColor,
						  onChange: onChangeBackgroundColor,
						  label: __( 'Background color', 'ka-example-block' )
						}
					] }
				/>
			</InspectorControls>
			<BlockControls>
				<AlignmentControl
					value={ align }
					onChange={ onChangeAlign }
				/>
			</BlockControls>
			<RichText 
				{ ...blockProps }
				tagName="p"
				onChange={ onChangeContent }
				allowedFormats={ [ 'core/bold', 'core/italic' ] }
				value={ content }
				placeholder={ __( 'Write your text...' ) }
				style={ { textAlign: align, backgroundColor: backgroundColor, color: textColor } }
			/>
		</>
	);
}

なお、RichText要素のstyle属性も更新します。

<RichText 
	 { ...blockProps }
	 tagName="p"
	 onChange={ onChangeContent }
	 allowedFormats={ [ 'core/bold', 'core/italic' ] }
	 value={ content }
	 placeholder={ __( 'Write your text...' ) }
	 style={ { textAlign: align, backgroundColor: backgroundColor, color: textColor } }
/>

ステップ3. block.jsonに必要な属性を定義する

ここで、block.jsonファイルにbackgroundColortextColor属性を定義します。

"attributes": {
	"content": {
		"type": "string",
		"source": "html",
		"selector": "p"
	},
	"align": {
		"type": "string",
		"default": "none"
	},
	"backgroundColor": {
		"type": "string"
	},	 
	"textColor": {
		"type": "string"
	}
},

ステップ4. イベントハンドラを定義する

次に、入力に応じてbackgroundColortextColorを更新する2つの関数を定義します。

const onChangeBackgroundColor = ( newBackgroundColor ) => {
	setAttributes( { backgroundColor: newBackgroundColor } )
}

const onChangeTextColor = ( newTextColor ) => {
	setAttributes( { textColor: newTextColor } )
}

ステップ5. データを保存する

最後にsave.jsファイルを開き、スクリプトを以下のように変更します。

export default function save( { attributes } ) {
	const blockProps = useBlockProps.save();
	const { content, align, backgroundColor, textColor } = attributes;
	return (
		<RichText.Content 
			{ ...blockProps } 
			tagName="p" 
			value={ content } 
			style={ { textAlign: align, backgroundColor: backgroundColor, color: textColor } }
		/>
	);
}

ファイルを保存して、エディターでブロックを確認します。ブロックに予期せぬ、または無効なコンテンツが含まれていることを知らせるエラーメッセージが表示される場合があります。

予期せぬ、または無効なコンテンツのエラーメッセージ
予期せぬ、または無効なコンテンツのエラーメッセージ

このエラーは、save.jsファイルが変更され、データベースに保存されたコードがエディターで使用されているコードと一致しない時に発生します。

これを修正するには、ページを更新し、ブロックのインスタンスを一度削除して、再度投稿に追加してください。

カラー設定パネル付きのカスタムブロック
カラー設定パネル付きのカスタムブロック

変更を加え、投稿を保存して、フロントエンドで表示します。ブロックエディターで行った変更が、サイトにも反映されているはずです。

フロントエンドでもカスタムブロックが正しく動作
フロントエンドでもカスタムブロックが正しく動作

次に、ブロックタイプに新しいコンポーネントを追加します。

  • カスタムブロックにカスタマイズ可能なリンクを追加できるようにするExternalLinkコンポーネント
  • リンクの設定をカスタマイズできる複数のサイドバーコントロール

ステップ1. @wordpress/componentsからコンポーネントをインストールする

@wordpress/componentsから複数のコンポーネントをインポートする必要があります。edit.jsを開き、以下のimport文を追加してください。

import {
	TextControl,
	PanelBody,
	PanelRow,
	ToggleControl,
	ExternalLink
} from '@wordpress/components';
  • PanelBody:設定サイドバーに折りたたみ可能なコンテナを追加
  • PaneRow:サイドバーコントロール用の汎用コンテナを生成
  • TextControl:テキスト入力用のコントロール
  • ToggleControl:特定のオプションの有効・無効を切り替えられるトグル用コントロール
  • ExternalLink:外部リンクを追加するシンプルなコンポーネント

ステップ2. JSXコードに対応する要素を追加する

まず、divコンテナ内に、RichTextと同じレベルでExternalLink要素を追加します。

<div { ...blockProps }>
	<RichText 
		...
	/>
	<ExternalLink 
		href={ kaLink }
		className="ka-button"
		rel={ hasLinkNofollow ? "nofollow" : "" }
	>
			{ linkLabel }
	</ExternalLink>

</div>

なお、ExternalLinkコンポーネントはドキュメント化されていないため、コンポーネントのソースコードを参照して利用可能な属性の一覧を取得しました。ここでは、hrefclassNamerel属性を使用しています。

デフォルトでは、rel属性の値はnoopener noreferrerに設定されます。このコードでは、トグルコントロールがオンのとき、結果のaタグのrel属性にnofollowキーワードが追加されます。

ブロックサイドバーにリンク設定を追加します。まず、InspectorControls内に、PanelColorSettingsと同じレベルでPanelBody要素を追加します。

<InspectorControls>
	<PanelColorSettings 
	...
	/>
	<PanelBody 
		title={ __( 'Link Settings' )}
		initialOpen={true}
	>
	...
	</PanelBody>
</InspectorControls>

今回は、以下を実行しています。

  1. title属性でパネルのタイトルを指定
  2. initialOpenで初期状態でパネルが開いているかどうかを設定

次に、PanelBodyの中に2つのPanelRow要素を追加し、それぞれのPanelRowの中にTextControl要素を追加します。

<PanelBody 
	title={ __( 'Link Settings', 'ka-example-block' )}
	initialOpen={true}
>
	<PanelRow>
		<fieldset>
			<TextControl
				label={__( 'KA link', 'ka-example-block' )}
				value={ kaLink }
				onChange={ onChangeKaLink }
				help={ __( 'Add your Academy link', 'ka-example-block' )}
			/>
		</fieldset>
	</PanelRow>
	<PanelRow>
		<fieldset>
			<TextControl
				label={__( 'Link label', 'ka-example-block' )}
				value={ linkLabel }
				onChange={ onChangeLinkLabel }
				help={ __( 'Add link label', 'ka-example-block' )}
			/>
		</fieldset>
	</PanelRow>
</PanelBody>

上のコードは、もうかなり簡単に思えるはず。それから、2つのテキストコントロールにリンクラベルとURLを設定します。

さらに、ToggleControlを含むPanelRowを追加して、属性を含めるかどうかなど、特定のオプションの有効、無効を切り替えられるようにします。

<PanelRow>
	<fieldset>
		<ToggleControl
			label="Add rel = nofollow"
			help={
				hasLinkNofollow
					? 'Has rel nofollow.'
					: 'No rel nofollow.'
			}
			checked={ hasLinkNofollow }
			onChange={ toggleNofollow }
		/>
	</fieldset>
</PanelRow>

ステップ3. block.jsonに必要な属性を定義する

block.jsonファイルにkaLinklinkLabelhasLinkNofollow属性を定義します。

"kaLink": {
	"type": "string",
	"default": ""
},
"linkLabel": {
	"type": "string",
	"default": "Check it out!"
},
"hasLinkNofollow": {
	"type": "boolean",
	"default": false
}

これ以上、何かを追加する必要はありません。イベントハンドラ関数の定義に移りましょう。

ステップ4. イベントハンドラを定義する

edit.jsファイルに戻り、以下の関数を追加します。

const { content, align, backgroundColor, textColor, kaLink, linkLabel, hasLinkNofollow } = attributes;

const onChangeKaLink = ( newKaLink ) => {
	setAttributes( { kaLink: newKaLink === undefined ? '' : newKaLink } )
}

const onChangeLinkLabel = ( newLinkLabel ) => {
	setAttributes( { linkLabel: newLinkLabel === undefined ? '' : newLinkLabel } )
}

const toggleNofollow = () => {
	setAttributes( { hasLinkNofollow: ! hasLinkNofollow } )
}

この関数は、入力に対応する属性値を更新します。

ステップ5. データを保存する

最後に、save.jssave関数を更新します。

export default function save( { attributes } ) {
	
	const { content, align, backgroundColor, textColor, kaLink, linkLabel, hasLinkNofollow } = attributes;

	const blockProps = useBlockProps.save( {
		className: `has-text-align-${ align }`
	} );
	
	return (
		<div 
			{ ...blockProps }
			style={ { backgroundColor: backgroundColor } }
		>
			<RichText.Content 
				tagName="p" 
				value={ content } 
				style={ { color: textColor } }
			/>
			<p>
				<a 
					href={ kaLink }
					className="ka-button"
					rel={ hasLinkNofollow ? "nofollow" : "noopener noreferrer" }
				>
					{ linkLabel }
				</a>
			</p>
		</div>
	);
}

なお、今回の例では、ExternalLinkの代わりに、通常のa要素を使用しています。

結果は以下のようになります。

ブロック設定サイドバーのリンク設定パネル
ブロック設定サイドバーのリンク設定パネル

複数のブロックスタイルの追加

前のセクションでは、テキストの配置を設定できる、ブロックツールバーコントロールの追加方法について学びました。同じ手順でブロックツールバーにさらにスタイルコントロールを追加できますが、ワンクリックで選択できる、定義済みのブロックスタイルを追加する方法もあります。

これにはブロックAPIの便利な機能ブロックスタイルを使用します。

block.jsonstylesプロパティを定義して、対応するスタイルをスタイルシートで宣言します。

例えば、次のようなスタイルの配列を追加できます。

"styles": [
	{
		"name": "default",
		"label": "Default",
		"isDefault": true
	},
	{
		"name": "border",
		"label": "Border"
	}
],

これで、デフォルトのスタイルと、border(枠)と呼ばれるスタイルが追加されました。ブロックエディターに戻ります。

2つの定義済みブロックスタイル
2つの定義済みブロックスタイル

スタイルは、ブロックスイッチャーをクリックし、ブロック設定サイドバーの「スタイル」パネルから利用できます。

スタイルを選択し、p要素に適用されるクラスを確認します。ブロック上で右クリックして、「検証」を選択してください。次の形式の名前の新しいクラスが追加されています。

is-style-{style-name}

Border」スタイルを選択すると、p要素にis-style-borderクラスが追加されます。「Default」スタイルを選択すると、代わりにis-style-defaultクラスが追加されます。

あとは、CSSのプロパティを宣言するだけです。editor.scssを開き、現在のスタイルを以下のように置き換えます。

.wp-block-ka-example-block-ka-example-block {
    padding: 4px;
}

次に、style.scssにも同様に追加します。前述したように、style.scssで定義されたスタイルは、フロントエンドとエディターの両方に適用されます。

.wp-block-ka-example-block-ka-example-block {
	&.is-style-default{
		border: 0;
        background-color: #FFE2C7;
	}
	&.is-style-border{
		border: 2px solid #000;
        border-radius: 16px;
        background-color: #F6F6F6;
	}
}

これで完了です。ページを更新して、新たなブロックスタイルをお楽しみください。

さまざまなブロックスタイル
さまざまなブロックスタイル

InnerBlocksコンポーネントを使用したGutebnergブロックのネスト

このカスタムブロックは、機能的には十分ですが、このままでは魅力的な外観とは言えません。訪問者の目を引く写真やグラフィックを追加することができます。

画像を追加すると、ブロックに複雑なレイヤーを重ねてしまう可能性がありますが、幸い一から作りなおす必要はありません。Gutenbergには、ネストしたブロック構造を作成する特別なコンポーネントがあります。

InnerBlocksコンポーネントは、以下のように定義されています。

InnerBlocksは、コンポーネントのペアをエクスポートし、ネストしたブロックコンテンツを実現するブロックを実装するもの。(英語原文の日本語訳)

まず、srcフォルダに.jsファイルを作成します。今回の例では、container.jsを作成します。

次に、新しいリソースをindex.jsファイルにインポートします。

import './container';

container.jsに戻り、必要なコンポーネントをインポートします。

import { registerBlockType } from "@wordpress/blocks";
import { __ } from "@wordpress/i18n";
import {
	useBlockProps, 
	InnerBlocks 
} from "@wordpress/block-editor";

次に、内部におけるブロックの配置を規定するテンプレートを定義します。この例では、2つのカラムからなるテンプレートを定義します。それぞれのカラムに、コアとなる画像ブロックとカスタムブロックを配置します。

const TEMPLATE = [ [ 'core/columns', { backgroundColor: 'yellow', verticalAlignment: 'center' }, [
	[ 'core/column', { templateLock: 'all' }, [
		[ 'core/image' ],
	] ],
	[ 'core/column', { templateLock: 'all' }, [
		[ 'ka-example-block/ka-example-block', { placeholder: 'Enter side content...' } ],
	] ],
] ] ];

テンプレートの構造は、blockTypes(ブロック名と任意の属性)の配列です。

上のコードでは、複数の属性を使用して、ColumnsブロックとColumnブロックを構成しています。特に、templateLock: 'all'属性は、Columnブロックをロックし、不用意に既存のブロックを追加、並べ替え、削除できないようにしています。templateLockは、以下のいずれかの値を取ります。

  • allInnerBlocksがロックされブロックの追加、並べ替え、削除ができなくなる
  • insert:ブロックの並べ替えと削除のみ可能
  • false:テンプレートがロックされない

テンプレートは、InnerBlocks要素に割り当てられます。

<InnerBlocks
	template={ TEMPLATE }
	templateLock="all"
/>

互換性の問題の回避のため、InnerBlocksコンポーネントにtemplateLock属性も追加します(issue #17262とpull #26128参照)。

最終的なcontainer.jsファイルは、以下の通りです。

registerBlockType('ka-example-block/ka-example-container-block', {
	title: __( 'KA Container block', 'ka-example-block' ),
	category: 'design',

	edit( { className } ) {
		
		return(
			<div className={ className }>
				<InnerBlocks
					template={ TEMPLATE }
					templateLock="all"
				/>
			</div>
		)
	},

	save() {
		const blockProps = useBlockProps.save();
		return(
			<div { ...blockProps }>
				<InnerBlocks.Content />
			</div>
		)
	},
});
エディター内での最終的なブロック
エディター内での最終的なブロック

細かな編集

これでブロックが機能するようになりましたが、さらにちょっとした編集を加えることで、ブロックを改良することもできます。

RichTextコンポーネントで生成される段落には、backgroundColor属性を割り当てましたが、コンテナのdivに背景色を割り当てた方が良いかもしれません。

その場合は、edit.jsファイルとsave.jsファイルのdivを以下のように変更します。

<div 
	{ ...blockProps }
	style={ { backgroundColor: backgroundColor } }
>
...
</div>

これでブロック全体の背景を変更できます。

一方、関連性の高い変更が、useBlockPropsメソッドです。今回の例では、元のコードでは、定数blockPropsを以下のように定義しました。

const blockProps = useBlockProps();

しかし、プロパティのセットを渡すことで、useBlockPropsを効果的に使用できます。例えば、classnamesモジュールからclassnamesをインポートして、それに応じたラッパークラス名を設定できます。

以下の例では、align属性の値に基づいてクラス名を割り当てています(edit.js)。

import classnames from 'classnames';

...

export default function Edit( { attributes, setAttributes } ) {
	...
	
	const onChangeAlign = ( newAlign ) => {
		setAttributes( { 
			align: newAlign === undefined ? 'none' : newAlign, 
		} )
	}

	const blockProps = useBlockProps( {
		className: `has-text-align-${ align }`
	} );
	...
}

save.jsも同様に変更します。

import classnames from 'classnames';

...

export default function save( { attributes } ) {
	...
	const { content, align, backgroundColor, textColor, kaLink, linkLabel, hasLinkNofollow } = attributes;

	const blockProps = useBlockProps.save( {
		className: `has-text-align-${ align }`
	} );
	...
}

これで終了です!本番用のビルドを実行しましょう。

npm run build

まとめ

ここまで、お読みいただきありがとうございました。今回の記事では、開発環境の設定から完全なブロックタイプの作成まで詳しくご紹介しました。

冒頭で述べたように、高度なGutenbergブロックを作成し、プロのGutenberg開発者になるには、Node.js、Webpack、Babel、Reactに精通していなければなりません。

しかし、Reactの経験が十分でなくても、ブロック開発を楽しむことはできます。実際に手を動かしていく中で、Gutenbergブロックに必要な幅広い技術スキルを磨くモチベーションは自ずと生まれるはずです。

この解説記事は、あくまで、はじめてのGutenbergブロック作成に役立つ情報のご紹介が目的であり、すべての関連トピックには触れていません。オンラインドキュメントや解説記事を利用して、知識を深めていってください。以下は、特におすすめのリソースです。

WordPressの開発を始めたばかりであれば、フロントエンド開発の基本的なコンセプトの理解も欠かせません。以下のリソースを学習にお役立てください。

また、この記事で取り上げたサンプルコードはGistで公開されていますので、ぜひご活用ください。

Gutenbergブロックを開発したことはありますか?これまでにどのような問題に遭遇されましたか?

Carlo Daniele Kinsta

ウェブデザインとフロントエンド開発をこよなく愛し、WordPress歴は10年以上。イタリアおよびヨーロッパの大学や教育機関とも共同研究を行う。WordPressに関する記事を何十件も執筆しており、イタリア国内外のウェブサイトや雑誌に掲載されている。詳しい仕事情報はXとLinkedInで公開中。