
WordPressのカスタムブロックを作った話
こんにちは!クリエイティブチームの新人フロントエンドエンジニアのYです!
私たちクリエイティブチームはWeb制作を担うチームで主なCMSはWordPressを採用することが多いです。
WordPressのブロックエディターであるGutenberg(グーテンベルク)には、標準で多くのブロックが用意されています。
段落、見出し、画像、リストなど、基本的なコンテンツ作成に必要なブロックは一通り揃っています。
しかし、実際のサイト制作では、標準ブロックだけでは実現できないデザイン要件や機能要件が発生することありませんか?私はあります。(現在進行系)
標準ブロックでは実現できないデザイン要件がある
現在私が携わっているプロジェクトでは、サイト固有のデザイン要件を満たすために標準ブロックでは対応できない部分が複数あります。(本来なら標準ブロックで賄えるよう情報を落とし込むのですが…)
例えば、特定の背景色を持つセクション領域を作成したい場合、標準ブロックでは限定的な対応しかできません。
グループブロックを使用して背景色を設定することはできますが、セクション全体の幅を自由に制御したり、内部コンテンツの幅を個別に設定したりすることは困難です。
また、関連ページをカード形式で表示する機能も、標準ブロックには含まれていません。
カラムブロックとカードブロックを組み合わせることで似たような見た目は実現できますが、OGP情報を自動取得したり、内部リンクと外部リンクを自動判定したりする機能は実装できません。
サイト固有のレイアウトパターンや、ブランドに合わせた独自のコンテンツ表示形式を実現するには、標準ブロックでは不十分な事が多いです。
企業サイトでは、特定のデザインガイドラインに従ったコンテンツ表示が必要になることが多く、標準ブロックだけでは要件を満たせないケースが頻繁に発生します。
これらの要件を標準ブロックだけで実現しようとすると、エディター側での設定が複雑になり、編集者の負担が増えてしまいます。
私たちがCMS案件を構築させていただく際に気をつけていることは「実際に運用していくのはお客様なので負担をなるべく減らしたい」という想いです。
カスタムブロックを使用することで解決
カスタムブロックを実装することで、それら複数の課題を同時に解決できるとしたらどうでしょうか。
まず、デザインの一貫性を保つことができます。
カスタムブロックを使用することで、編集者が独自のHTMLやCSSを記述する必要がなくなり、意図しないマークアップの混入を防げます。
標準ブロックだけを使用している場合、たまに編集者がHTMLブロックを使用して独自のマークアップを記述することがありますが、これによりデザインの一貫性が損なわれ、サイト全体の見た目がバラバラになる可能性があります。カスタムブロックを使用することで、この問題を解決できます。
次に、コンテンツ制作の効率化が図れます。
よく使用するレイアウトパターンをカスタムブロックとして定義しておくことで、毎回同じ構造を手作業で作成する手間を省けます。
例えば、今回作ったセクションブロックを使用することで、背景色や幅を設定したセクション領域を、わずか数クリックで作成できます。
また、エディター上で視覚的に確認しながら編集できるため、フロントエンドでの表示結果をイメージしやすくなります。
これにより、編集作業の効率が向上し、コンテンツ制作にかかる時間を大幅に短縮できます。
カスタムブロックの基本的な仕組み
技術者的にはカスタムブロックって作るのが難しそうでどうやって作るの?と思われる方が多いかと思います。私もそうでした。
多くの人は「Genesis Custom Blocks」などのカスタムブロックを作成できるプラグインを使用するかと思います。
私も少し前までそうでした。しかし、余りプラグインを多く使用したくない & プラグインだと痒いところに手が届かないことや、機能過多な部分があり、細かな調整ができませんでした。
というわけで、今回は自前でカスタムブロックを実装してみました。
WordPressのカスタムブロックは、block.jsonというメタデータファイルと、JavaScriptによるエディター実装、PHPによるフロントエンド表示の実装で構成されます。
block.jsonでは、ブロックの名前、タイトル、アイコン、カテゴリ、属性などの基本情報を定義し、JavaScript側では、エディター上でのブロックの編集インターフェースを実装します。
Reactを使用して、ユーザーがブロックの設定を変更できるUIを構築します。
PHP側では、フロントエンドでのブロックの表示内容を制御します。サーバーサイドレンダリングを活用することで、データベースから取得した情報や外部APIから取得した情報を、フロントエンドに表示することができます。
この3つの要素を組み合わせることで、エディター体験とフロントエンド表示を統一しつつ、柔軟な実装が可能になります。
それぞれの役割を整理すると、以下のようになります。
| block.json | ブロックのメタデータ定義 | 名前、タイトル、アイコン、属性などの基本情報 |
| JavaScript | エディター側の実装 | Reactを使用した編集インターフェースの構築 |
| PHP | フロントエンド側の実装 | サーバーサイドレンダリングによる表示内容の制御 |
block.jsonはブロックの設計図のような役割を果たし、JavaScriptとPHPはそれぞれエディター側とフロントエンド側の実装を担当します。この3つの要素を組み合わせることで、エディター体験とフロントエンド表示を統一しつつ、柔軟な実装が可能になります。
カスタムブロックの設計方針
カスタムブロックを実装する際には、WordPress推奨の方法に準拠しながら、既存のテーマ構造やPHPロジックとの連携を考慮した設計が重要です。
block.jsonによるメタデータ中心の設計
block.jsonでは、ブロックの基本情報、属性、サポート機能などを定義します。このファイルを中心に設計することで、WordPress推奨の方法に準拠しながら、ブロックの設定を一元管理できます。
ブロックの名前、タイトル、アイコン、カテゴリ、説明文、キーワードなどの基本情報を記述し、attributesセクションで、ブロックが持つ属性を定義します。
例えば、今回のセクションブロックでは、backgroundColorとisFullWidthという2つの属性を定義しました。
supportsセクションでは、ブロックがサポートする機能を定義します。
htmlをfalseに設定し、カスタムHTMLを禁止することで、意図しないマークアップの混入を防止し、alignをwideとfullに設定することで、幅の調整機能を有効にしています。
エディター体験とフロントエンド表示の統一
カスタムブロックを実装する際には、エディター上での表示とフロントエンドでの表示をできるだけ統一することが重要です。
エディター上で見た目と実際のサイトでの見た目が大きく異なると、編集者がコンテンツを作成する際に混乱を招く可能性があるからです。
基本的にエディター上とフロントエンドは同じCSSを読み込む(ファイルをわけるにしても同じ内容を記述)ことを推奨します。こうすることでスタイルの一貫性を保ち編集者はエディター上で見た目を確認しながら、コンテンツを作成できるようになります。
サーバーサイドレンダリングによる柔軟な実装
カスタムブロックでは、サーバーサイドレンダリングを活用することで、柔軟な実装が可能になります。
JavaScriptのみで実装する静的ブロックでは、データベースから取得した情報や外部APIから取得した情報を表示することが難しくなるので、サーバーサイドレンダリングを採用しPHP側でデータを取得し、フロントエンドに表示することが可能になります。
今回作成した関連ページブロックでは、URLを入力すると、PHP側で内部リンクと外部リンクを判定し、外部リンクの場合はOGP情報を取得して表示する機能を実装しました。
これにより、エディター側ではURLを入力するだけで、フロントエンド側で適切な情報を表示できるようになります。
既存テーマロジックとの連携方法
カスタムブロックを実装する際には、既存のテーマ構造やPHPロジックとの連携を考慮する必要があります。
関連ページブロックでは、すでに実装されていた既存のouterUrl関数を使用してOGP情報を取得する機能を実装しました。render.phpでは、WordPressのサニタイズ関数であるesc_url、esc_attr、esc_htmlなどを必ず使用することで、セキュリティを確保しています。
カスタムブロックの弱点としてCSS(スタイル)を自前で用意する必要があるので既存のテーマに組み込む際は気をつけなくてはいけません。
場合によってはデザイナーの力を頼る必要性が出てきます。
やってみよう!カスタムブロック
今回のプロジェクトでは、セクションブロック・関連ページブロック・リンクカードブロック・カードブロックの4つを実装しましたが、ここでは、セクションブロックと関連ページブロックの2つの実装例を取り上げます。
実装過程で、サニタイズ処理やスタイルの統一、キャッシュ戦略など、いくつかのポイントで詰まりましたが、それぞれの課題に対して適切な解決方法を見つけることができました。
カスタムブロックの実装は、ブロックディレクトリの構成から始まります。各ブロックは専用のディレクトリを持ち、その中にblock.json、index.js、editor.css、style.css、render.phpを配置します。
ブロックディレクトリの構成
今回のプロジェクトでは、src/gutenbergディレクトリ配下に、各ブロック用のディレクトリを作成しました。
セクションブロックはsrc/gutenberg/section-blockディレクトリに、関連ページブロックはsrc/gutenberg/related-page-blockディレクトリに配置しました。
ブロックディレクトリには、以下の5つのファイルを配置します。
| block.json | ブロックのメタデータを定義するファイル。ブロックの基本情報、属性、サポート機能を定義 |
| index.js | エディター側の実装を行うJavaScriptファイル。Reactを使用したエディター側のUI実装 |
| editor.css | エディター側のスタイルを定義するCSSファイル。エディター上での表示用のCSS |
| style.css | フロントエンド側のスタイルを定義するCSSファイル。サイト上での表示用のCSS |
| render.php | フロントエンド側の表示を制御するPHPファイル。サーバーサイドレンダリングによる表示制御 |
セクションブロックの実装例
セクションブロックは、内部に他のブロックを配置できるコンテナブロックです。
「セクション」の名の通り、コンテンツ幅をぶち抜いて画面いっぱいに背景色をを設定できるため、ページ全体のレイアウトが単調になりがちな課題を解決できます。

block.jsonの設定方法
あらためてblock.jsonのおさらいをします。
このファイルでブロックの基本情報、属性、サポート機能などを定義します。
セクションブロックのblock.jsonでは、name、title、category、icon、description、keywordsなどの基本情報を定義し、nameはcustom/sectionとし、カスタムブロックであることを明示します。
titleはセクションとし、エディター上で表示されるブロック名を定義しています。
categoryはdesignとし、デザインカテゴリに分類しました。
iconはlayoutとし、レイアウト関連のブロックであることを示すアイコンを設定しました。

attributesセクションでは、backgroundColorとisFullWidthという2つの属性を定義しました。backgroundColorは文字列型で、背景色を設定する属性です。
isFullWidthは真偽値型で、セクション幅をフル幅にするかどうかを設定する属性です。
supportsセクションでは、htmlをfalseに設定し、カスタムHTMLを禁止しました。
また、alignをwideとfullに設定することで、幅の調整機能を有効にしています。
JavaScript側のエディター実装
セクションブロックでは、registerBlockTypeを使用してブロックを登録し、edit関数とsave関数を実装しています。
エディター実装で使用する主なコンポーネントには、registerBlockType、InspectorControls、ColorPicker、ToggleControl、useBlockProps、InnerBlocksなどがあります。
- registerBlockTypeはブロックの登録を行うコンポーネントで、ブロックの基本情報とedit/save関数を登録します
- InspectorControlsは設定パネルを表示するコンポーネントで、ブロックの詳細設定を表示します
- ColorPickerは色選択を行うコンポーネントで、背景色などの色設定に使用します
- ToggleControlは真偽値設定を行うコンポーネントで、フル幅設定などのオン/オフ切り替えに使用します
- useBlockPropsはブロック属性を設定するコンポーネントで、クラス名やスタイルの設定に使用します。
- InnerBlocksは内部ブロックを配置するコンポーネントで、ブロック内部に他のブロックを配置する際に使用します。
これらのコンポーネントを組み合わせることで、編集者が直感的に操作できるエディターインターフェースを構築できるので、編集者の操作負担を最小限に抑えることが重要です。
InnerBlocksを活用したレイアウトブロック
InnerBlocksを使用することで、ブロック内部に他のブロックを配置できるレイアウトブロックを実装できます。
今回のセクションブロックでは、InnerBlocksを使用して、セクション内部に任意のブロックを配置できるようにしました。
こうすることでセクションブロック自体の設定は背景色と幅設定のみになり、実装や権限を明確化できます。
内部のブロックエディタには当然見出しブロックや段落ブロック、画像ブロックなどを配置できます。
InnerBlocksを活用することで、再利用可能なレイアウトパターンを提供できます。セクションブロックのように、背景色や幅を設定できるラッパーブロックを実装することで、サイト全体で一貫したデザインを維持しやすくなります。
関連ページブロックの実装例
関連ページブロックは、URLを入力するとOGP情報を自動取得し、カード形式で表示するブロックです。

block.jsonでは、url、ogpImage、ogpTitle、ogpDescriptionの4つの属性を定義しました。
urlは文字列型で、関連ページのURLを指定します。
ogpImage、ogpTitle、ogpDescriptionは文字列型で、OGP情報を格納します。
JavaScript側では、URL入力フィールドを実装し、ユーザーがURLを入力できるようにしました。TextControlコンポーネントを使用して、URL入力フィールドを実装し、onChangeプロパティで属性を更新します。
OGP情報は、PHP側で取得するため、エディター側では属性の値を表示するだけです。
これにより、エディター側の負荷を軽減し、最新の情報を常に取得できます。
エディター側でOGP情報を取得しようとすると、CORSの問題や、エディター側のパフォーマンスへの影響が懸念されますが、PHP側で実装することで、これらの問題を回避できます。
PHP側では、まずURLが内部リンクか外部リンクかを判定します。URLがルートパスで始まる場合、またはホスト名が現在のサイトと同じ場合は内部リンクと判定します。
$_SERVER[‘HTTP_HOST’]を使用して、現在のサイトのホスト名を取得し、parse_url関数を使用して、入力されたURLのホスト名を取得します。
内部リンクの場合は、url_to_postid関数を使用して投稿IDを取得し、WordPressの標準関数で投稿情報を取得します。
get_post関数を使用して投稿情報を取得し、get_the_title関数やwp_trim_words関数を使用して、タイトルや説明文を取得します。
外部リンクの場合は、テーマ側で定義されたouterUrl関数を使用してOGP情報を取得します。
この関数は、外部URLにアクセスしてOGPタグを解析し、画像、タイトル、説明文を取得します。
取得した情報は、属性に保存されている場合はそれを使用し、保存されていない場合は新しく取得した情報を使用します。
OGPタグは、HTMLのheadセクションに記述されるメタタグであり、og:image、og:title、og:descriptionなどのプロパティを含みます。
これらのプロパティを解析することで、外部URLの情報を取得できます。
カスタムブロック実装時の注意点
カスタムブロックを実装する際は、セキュリティ、パフォーマンス、保守性、エディター体験の4つの観点から注意点を把握しておく必要があります。
セキュリティ対策とサニタイズ処理
カスタムブロックを実装する際の最も重要な注意点は、セキュリティ対策です。
ユーザー入力や外部データをそのまま出力すると、XSS攻撃などの脆弱性が発生する可能性があります。
ご存知でない方もいらっしゃると思いますが、XSS攻撃は悪意のあるスクリプトをWebページに注入し、ユーザーのブラウザで実行させる攻撃手法です。
WordPressでは、esc_url、esc_attr、esc_htmlなどのサニタイズ関数が用意されていますので、すべての出力に対して、サニタイズ関数を必ず使用します。
| サニタイズ関数 | 用途 | 使用例 |
| esc_url | URLのサニタイズ | リンクのhref属性に使用 |
| esc_attr | HTML属性のサニタイズ | class属性やstyle属性に使用 |
| esc_html | HTMLコンテンツのサニタイズ | テキストコンテンツの出力に使用 |
また、supports.htmlをfalseに設定することで、カスタムHTMLの入力を禁止し、意図しないマークアップの混入を防ぎます。
さらに、wp_kses関数を使用して、許可されたHTMLタグのみを出力することで、より厳密なセキュリティ対策を実装できます。
これらの対策により、カスタムブロックのセキュリティを強化し、XSS攻撃などの脆弱性を防ぐことができます。
パフォーマンスへの配慮
カスタムブロックを実装する際は、パフォーマンスへの配慮も重要です。
特に、外部データの取得や、複雑な処理を行う場合は、キャッシュ戦略を検討する必要があります。
パフォーマンスが低下すると、ユーザー体験が損なわれ、サイトの離脱率が高まる可能性があります。
関連ページブロックでは、外部URLのOGP情報を取得する処理を実装したことは先程も述べました。
この処理は、毎回実行するとパフォーマンスに影響を与える可能性があるため、キャッシュ戦略を検討する必要があります。
WordPressのTransients APIを使用して、取得したOGP情報を一時的に保存し、一定期間内はキャッシュから取得することで、パフォーマンスを向上させます。
ただし、古い情報が表示されないようにする必要があるので、キャッシュの有効期限は、データの更新頻度に応じて設定し1時間から24時間程度が適切です。
また、JavaScriptファイルやCSSファイルのサイズにも注意が必要です。
必要最小限のコードのみを含めることで、ページの読み込み速度を向上させます。Gulpなどのビルドツールを使用して、コードの最適化を行うことを推奨します。コードの圧縮や、不要なコードの削除、Tree Shakingなどの最適化手法を適用することで、ファイルサイズを削減できます。
保守性を高める設計パターン
カスタムブロックを実装する際には、保守性を高める設計パターンを採用することが重要です。block.jsonを中心に設計することで、WordPress推奨の方法に準拠しながら、ブロックの設定を一元管理したり、各ブロックを独立したディレクトリに配置することで、ブロックごとの管理が容易になり、保守性が向上させましょう。
機能開発全般に言えることですがコメントを適切に記述し、関数の役割を明確にすることで、後からコードを読む人が理解しやすくなります。
特に、複雑な処理を行う場合は、処理の流れをコメントで説明することが重要です。PHPDoc形式のコメントを使用して、関数の引数や戻り値の型、処理の説明を記述することで、IDEでの補完機能を活用できます。
また、変数名や関数名も、その役割が明確にわかるような名前を付けることで、コードの可読性が向上します。
まとめ
これまでWordPressのカスタムブロックについてお話をしてきました。技術解説書にならないよう気をつけてきましたがいかがでしたでしょうか。
カスタムブロックを実装することで、サイトの表現力と編集体験が向上します。
今回実装してみて昔よりかなり作りやすくなっていることが実感できました。
プラグインを使用してカスタムブロックを作るのもありですがより柔軟にお客様へコミットするために、自前で実装するのもありだと思います。
ネットアシストでは、クリエイティブチームが発足し、サーバー構築だけでなくサイト制作全般を一括でサポートできる体制が整いました。
WordPressサイトのテーマ開発やカスタマイズやカスタムブロックの実装に関するご相談がございましたら、ぜひお問い合わせください。
またサイトの保守ももちろんご相談可能です。





