kajimaru note

カスタムブロックの作り方(WordPress編)

WordPressのGutenberg(ブロックエディター)で記事を作成していると、元から用意されている標準のブロックでは理想の見た目にならず、仕方なく【カスタムHTML】ブロックを使ってインラインでコードを書くことがあります。

ただ、毎回コードを書くのも面倒だし、編集画面も見にくくなるので、繰り返し使える汎用的なブロックを自作しようと思ったときのための備忘録。

Genesis Custom Blocks」など、手軽にカスタムブロックを作成できるプラグインもありますが、今回は編集画面側のUIにも手を加えたいので、WordPressのブロック開発環境を使ってReact / JSXで自作します。

ざっくり手順と準備

★超ざっくり手順

  1. ブロックの構成ファイルを作成する
  2. 構成ファイルをビルドする
  3. ビルドしたファイルをサーバーにアップロードする
  4. functions.phpに追記する

★用意するもの

  • コードエディター
    手順1でブロックの構成ファイルを作成するのに必要なアプリケーション。
    例:Visual Studio Code / Dreamweaver / Atom / Sublime Text など
    Windows標準のメモ帳、Mac標準のテキストエディットでも可能ですが、コードエディターの方が圧倒的に楽です。
  • ビルド環境
    手順2で構成ファイルをビルドするための環境。
    この記事では「@wordpress/scripts」を使ったビルド手順で解説します。
    インストール手順は後述
  • FTPクライアント
    手順3でビルドしたファイルをサーバーにアップロードするために必要なアプリケーション。
    例:FFFTP / WinSCP / FileZilla / Cyberduck など

★@wordpress/scriptsの導入手順

「@wordpress/scripts」はNode.jsの npm を使ってインストールできます。
Node.jsをインストールしていない場合は、下記の手順に入る前に、まずNode.jsを入れておく必要があります。(Node.jsのインストール方法はここでは割愛)

  1. Windowsの場合はコマンドプロンプト、Macの場合はターミナルを開く

  2. プロジェクト(Wordpressテーマ)のディレクトリに移動する

  3. npm init を実行する
    npm init -y
  4. @wordpress/scriptsをインストールする
    npm install --save-dev @wordpress/scripts sass concurrently ※複数の処理を同時に実行する場合に備えて、 concurrently も同時にインストールしています。

これで@wordpress/scriptsのインストールは完了です。

1. ブロックの構成ファイルを作成する

まずはカスタムブロックを構成するのに必要な下記5つのファイルをコードエディターで作成します。

この記事では「記事内に挿入する画像をPCとスマホで出し分けられるブロック」を作りますが、基本的な手順は他のブロックを作るときも同じです。
(JSファイルはReact / JSXで記述しますが、ReactやJSXにあまり詳しくなくても、AIにコードを生成してもらいながら進めれば大丈夫です。)

作成するブロック構成ファイル

  • package.json
    ビルド環境の設定ファイル。使用するライブラリやビルドのコマンドなどを定義します。ブロック開発に直接関係はなく、ビルドのための設定です。
  • block.json
    ブロックの定義ファイル。ブロック名・アイコン・カテゴリ・保存する属性(データ)などを定義します。WordPressがこのファイルを読んでブロックを認識します。
  • index.js
    エントリーポイント。block.json、edit.js、save.jsを読み込んでWordPressにブロックとして登録するだけのファイルです。基本的に変更不要です。
  • edit.js
    管理画面のブロックエディタで表示される見た目と操作UIを定義するファイル。画像選択ボタンやテキスト入力など編集UIをここで作ります。
  • save.js
    フロントエンドに出力されるHTMLを定義するファイルです。edit.jsで入力したデータをどんなHTML構造で出力するかをここで決めます。

各ファイルの配置(ディレクトリ構成)

{ WordPressテーマ名 }
├─ package.json
└─ blocks
   └─ { 作成するブロック名 }
      ├─ block.json
      ├─ index.js
      ├─ edit.js
      └─ save.js

※今回は「kajimaru」というWordPressテーマの中に、「responsive-image」というブロック名で作成しようと思うので、 { WordPressテーマ名 }kajimaru{ 作成するブロック名 }responsive-image になります。

※上記は blocks というディレクトリ名にしていますが、ディレクトリ名は任意です。ただし、後述する package.json の設定と合わせる必要があります。

各ファイルのサンプルコード

package.json

{
  "name": "kajimaru",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "sass": "sass assets/scss/style.scss assets/css/style.css --style=compressed",
    "sass:watch": "sass assets/scss/style.scss assets/css/style.css --watch",
    "build": "wp-scripts build blocks/responsive-image/index.js --output-path=blocks/responsive-image/build",
    "start": "wp-scripts start blocks/responsive-image/index.js --output-path=blocks/responsive-image/build"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@wordpress/scripts": "^34.1.0",
    "sass": "^1.102.0"
  }
}

block.json

{
  "$schema": "https://schemas.wp.org/trunk/block.json",
  "apiVersion": 3,
  "name": "kajimaru/responsive-image",
  "title": "Responsive Image",
  "category": "media",
  "icon": "format-image",
  "description": "PCとSPで別々の画像を設定できる画像ブロック",
  "attributes": {
    "pcImageUrl": {
      "type": "string",
      "default": ""
    },
    "pcImageAlt": {
      "type": "string",
      "default": ""
    },
    "spImageUrl": {
      "type": "string",
      "default": ""
    },
    "spImageAlt": {
      "type": "string",
      "default": ""
    },
    "caption": {
      "type": "string",
      "default": "" 
    }
  },
  "editorScript": "file:./build/index.js",
  "supports": {
    "html": false
  }
}

index.js

import { registerBlockType } from '@wordpress/blocks';
import Edit from './edit';
import save from './save';
import metadata from './block.json';

registerBlockType(metadata.name, {
  edit: Edit,
  save,
});

edit.js

import { useBlockProps, MediaUpload, MediaUploadCheck, RichText } from '@wordpress/block-editor';
import { Button } from '@wordpress/components';

export default function Edit({ attributes, setAttributes }) {
  const { pcImageUrl, pcImageAlt, spImageUrl, spImageAlt, caption } = attributes;
  const blockProps = useBlockProps();

  return (
    <div {...blockProps}>

      {/* PC画像 */}
      <div>
        <p><strong>PC用画像(769px以上)</strong></p>
        <MediaUploadCheck>
          <MediaUpload
            onSelect={(media) => setAttributes({ pcImageUrl: media.url, pcImageAlt: media.alt })}
            allowedTypes={['image']}
            render={({ open }) => (
              <div>
                {pcImageUrl
                  ? <img src={pcImageUrl} alt={pcImageAlt} style={{ maxWidth: '100%' }} />
                  : <Button onClick={open} variant="secondary">PC用画像を選択</Button>
                }
                {pcImageUrl && <Button onClick={open} variant="link">PC用画像を変更</Button>}
              </div>
            )}
          />
        </MediaUploadCheck>
      </div>

      {/* SP画像 */}
      <div style={{ marginTop: '16px' }}>
        <p><strong>SP用画像(768px以下)</strong></p>
        <MediaUploadCheck>
          <MediaUpload
            onSelect={(media) => setAttributes({ spImageUrl: media.url, spImageAlt: media.alt })}
            allowedTypes={['image']}
            render={({ open }) => (
              <div>
                {spImageUrl
                  ? <img src={spImageUrl} alt={spImageAlt} style={{ maxWidth: '100%' }} />
                  : <Button onClick={open} variant="secondary">SP用画像を選択</Button>
                }
                {spImageUrl && <Button onClick={open} variant="link">SP用画像を変更</Button>}
              </div>
            )}
          />
        </MediaUploadCheck>
      </div>

      {/* キャプション */}
      <RichText
        tagName="figcaption"
        value={caption}
        onChange={(value) => setAttributes({ caption: value })}
        placeholder="キャプションを入力(任意)"
        style={{ marginTop: '8px' }}
      />

    </div>
  );
}

save.js

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

export default function save({ attributes }) {
  const { pcImageUrl, pcImageAlt, spImageUrl, spImageAlt, caption } = attributes;

  return (
    <figure {...useBlockProps.save({ className: 'responsive-image' })}>
      <picture>
        {spImageUrl && (
          <source media="(max-width: 768px)" srcSet={spImageUrl} />
        )}
        {pcImageUrl && (
          <img src={pcImageUrl} alt={pcImageAlt} />
        )}
      </picture>
      {caption && (
        <RichText.Content tagName="figcaption" value={caption} />
      )}
    </figure>
  );
}

2. 構成ファイルをビルドする

Windowsの場合はコマンドプロンプト、Macの場合はターミナルで、 package.json があるディレクトリに移動し、下記のコマンドを実行します。

npm run build

コマンド実行後、 webpack ... compiled successfully のように successfully と表示されればビルド成功です。

コマンドの実行結果
フォルダ内にビルドされたファイルが生成されます

3. ビルドしたファイルをサーバーにアップロードする

FTPでサーバー上のWordPressテーマフォルダ配下に、ローカルで作成した blocks フォルダをそのままアップロードします。

4. functions.phpに追記する

functions.php に下記のコードを追記し、作成したカスタムブロックをWordPressに登録します。

function kajimaru_register_blocks() {
  register_block_type( get_template_directory() . '/blocks/responsive-image' );
}
add_action( 'init', 'kajimaru_register_blocks' );

'/blocks/responsive-image' のところは、作成したブロックのディレクトリに合わせて書き換えてください。

また、kajimaru_register_blocks は任意の関数名なので、必要に応じて適宜変更してください。

上記のコードを追記したら、FTPで functions.php をサーバーにアップロードして上書きします。

ビルドしたブロック構成ファイルと functions.php に問題がなければ、これで記事編集画面に作成したカスタムブロックが追加されます。
これでひとまず、カスタムブロックを使える状態になりました。

最後に動作確認をおこなって完成です。お疲れ様でした。

補足: 複数のカスタムブロックを作成する場合は、package.jsonfunctions.php の設定を適宜追加・変更することで対応できます。