カスタムブロックを作っていると、「最初にレイアウトを選ばせたい」という場面が出てきます。2カラムか3カラムか、画像が左か右か。こうした選択肢をテキストのボタンで並べても、選んだ結果がどうなるのかは伝わりません。
そこで使えるのが BlockPreview です。ブロックの構成データを渡すだけで、そのブロックが実際にどう表示されるのかを縮小して描画してくれます。コアの「パターン」や「バリエーションの選択画面」で見かける、あのサムネイルを自分のブロックでも作れます。
この記事では、BlockPreview の基本的な使い方と2つのプロパティ、そして選んだレイアウトをそのままブロックに流し込む実装例までを解説します。
目次
BlockPreview は表示専用のプレビュー用コンポーネント
BlockPreview は @wordpress/block-editor パッケージに含まれるコンポーネントです。渡されたブロックを描画しますが、編集はできません。クリックしても選択状態にならず、文字を打ち込むこともできない、見た目を確認するためだけの表示です。
用途としては次のような場面が向いています。
- 複数のレイアウト候補を、実際の見た目で選ばせる
- 設定を変えたときの結果を、サイドバーの中で先に見せる
- 自作のパターン一覧画面でサムネイルを並べる
逆に、実際に編集させたい場所では使いません。編集可能な入れ子が必要なら InnerBlocks を使います。
基本の使い方
インポートして、blocks にブロックのデータを渡すだけです。渡すのは HTML 文字列ではなく、createBlock() で作ったブロックオブジェクト(またはその配列)です。
import { useBlockProps, BlockPreview } from '@wordpress/block-editor';
import { createBlock } from '@wordpress/blocks';
export default function Edit() {
// プレビューしたいブロックの構成を作る
const previewBlocks = [
createBlock( 'core/heading', { content: 'キャンペーンのお知らせ', level: 3 } ),
createBlock( 'core/paragraph', { content: '9月30日まで実施しています。' } ),
];
return (
<div { ...useBlockProps() }>
<BlockPreview blocks={ previewBlocks } viewportWidth={ 800 } />
</div>
);
}
これだけで、見出しと段落が実際のスタイルで表示されます。プレビューの中身はコンテナの幅に合わせて縮小されるので、狭いサイドバーの中に置いても崩れません。
受け取るプロパティ
| プロパティ | 型 | 説明 |
|---|---|---|
blocks | オブジェクト/配列 | プレビューするブロック。1つでも配列でもよい |
viewportWidth | 数値 | プレビューを描画するときの想定ビューポート幅(px)。既定値は 700 |
viewportWidth は「何 px の画面で表示したものとして描くか」という指定です。実際の表示領域の幅ではありません。たとえば viewportWidth={ 360 } にすると、スマートフォン幅で見たときのレイアウトがプレビューされます。カラムが縦積みになるかどうかを見せたいときに使い分けます。
レイアウトを選ばせる UI を作る
実際によく作るのは、ブロックを挿入した直後に「どの構成にしますか」と選ばせるプレースホルダーです。選択肢ごとに BlockPreview を並べ、クリックされたらその構成を InnerBlocks として流し込みます。
まず、選択肢の定義です。ブロックの構成を作る関数として持っておくと、プレビュー用と挿入用で同じ定義を使い回せます。
import { createBlock } from '@wordpress/blocks';
export const LAYOUTS = [
{
name: 'two-columns',
label: '2カラム',
// 呼び出すたびに新しいブロックを作る(使い回すと clientId が重複する)
getBlocks: () => [
createBlock( 'core/columns', {}, [
createBlock( 'core/column', {}, [
createBlock( 'core/heading', { content: '左の見出し', level: 3 } ),
] ),
createBlock( 'core/column', {}, [
createBlock( 'core/paragraph', { content: '右側の説明文です。' } ),
] ),
] ),
],
},
{
name: 'stacked',
label: '縦並び',
getBlocks: () => [
createBlock( 'core/heading', { content: '見出し', level: 3 } ),
createBlock( 'core/paragraph', { content: '本文が入ります。' } ),
createBlock( 'core/buttons', {}, [ createBlock( 'core/button' ) ] ),
],
},
];
createBlock() の第3引数に配列を渡すと、その要素が入れ子のブロックになります。カラムブロックのように階層のある構成も、この形でそのまま書けます。
次に edit.js です。まだ中身が空のときは選択画面を出し、選ばれたら replaceInnerBlocks で流し込みます。
import {
useBlockProps,
useInnerBlocksProps,
BlockPreview,
store as blockEditorStore,
} from '@wordpress/block-editor';
import { Placeholder, Button } from '@wordpress/components';
import { useSelect, useDispatch } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import { LAYOUTS } from './layouts';
export default function Edit( { clientId } ) {
const blockProps = useBlockProps();
const innerBlocksProps = useInnerBlocksProps( blockProps );
const { replaceInnerBlocks } = useDispatch( blockEditorStore );
// 中身が空かどうかを判定する
const hasInnerBlocks = useSelect(
( select ) =>
select( blockEditorStore ).getBlocks( clientId ).length > 0,
[ clientId ]
);
if ( hasInnerBlocks ) {
return <div { ...innerBlocksProps } />;
}
return (
<div { ...blockProps }>
<Placeholder label={ __( 'レイアウトを選択', 'my-blocks' ) }>
<div className="my-layout-picker">
{ LAYOUTS.map( ( layout ) => (
<Button
key={ layout.name }
className="my-layout-picker__item"
onClick={ () =>
replaceInnerBlocks( clientId, layout.getBlocks(), false )
}
>
<BlockPreview
blocks={ layout.getBlocks() }
viewportWidth={ 800 }
/>
<span>{ layout.label }</span>
</Button>
) ) }
</div>
</Placeholder>
</div>
);
}
ポイントは、プレビュー用と挿入用で getBlocks() をそれぞれ呼んでいるところです。createBlock() は呼ぶたびに新しい clientId を持つブロックを返します。同じオブジェクトをプレビューにも挿入にも使い回すと、エディター内に同じ ID のブロックが2つ存在する状態になり、思わぬ不具合の原因になります。
replaceInnerBlocks の第3引数は「挿入したブロックを選択状態にするか」の指定です。false にしておくと、挿入直後に中の最初のブロックへフォーカスが飛ぶのを防げます。
プレビューを並べるスタイル
選択肢を横に並べるだけなら、エディター用のスタイルシートに数行書けば十分です。
.my-layout-picker {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 16px;
width: 100%;
}
.my-layout-picker__item {
display: block;
height: auto;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
&:hover {
border-color: var(--wp-admin-theme-color);
}
}
設定の変更結果を先に見せる
もう1つの使い道が、サイドバーでの「変更後のプレビュー」です。現在のブロックの属性から表示用のブロックを組み立てて渡せば、適用前に結果を確認させられます。
import { InspectorControls, BlockPreview } from '@wordpress/block-editor';
import { PanelBody, SelectControl } from '@wordpress/components';
import { createBlock } from '@wordpress/blocks';
import { useMemo } from '@wordpress/element';
// attributes.style が変わるたびにプレビュー用のブロックを作り直す
const preview = useMemo(
() =>
createBlock( 'core/quote', {
value: `<p>${ attributes.sampleText }</p>`,
className: `is-style-${ attributes.style }`,
} ),
[ attributes.sampleText, attributes.style ]
);
return (
<InspectorControls>
<PanelBody title="表示スタイル">
<SelectControl
value={ attributes.style }
options={ [
{ label: 'デフォルト', value: 'default' },
{ label: '大きめ', value: 'large' },
] }
onChange={ ( style ) => setAttributes( { style } ) }
/>
<BlockPreview blocks={ preview } viewportWidth={ 600 } />
</PanelBody>
</InspectorControls>
);
useMemo を挟んでいるのは、再レンダリングのたびに createBlock() が走って新しいブロックが作られるのを避けるためです。依存配列に入れた属性が変わったときだけプレビューが作り直されます。
プレビューが思ったとおりに出ないとき
何も表示されない
まず blocks に渡しているものを確認してください。HTML 文字列や、保存済みの投稿本文をそのまま渡しても描画されません。createBlock() で作ったオブジェクト、あるいは parse() で本文を解析した結果の配列である必要があります。
import { parse } from '@wordpress/blocks';
// 保存済みのブロックコメント付き HTML から復元する
const blocks = parse( savedContent );
<BlockPreview blocks={ blocks } />;
もう1つよくあるのが、登録されていないブロックを指定しているケースです。プラグインが提供するブロック名を渡しても、そのプラグインが有効でなければ描画されません。コアブロックで一度試してみると切り分けられます。
フロントと見た目が違う
BlockPreview が描くのはエディター内です。テーマがフロント側だけで読み込んでいる CSS は当たらないため、実際の公開ページと完全に同じにはなりません。あくまで構成を確認するためのものと考えてください。
近づけたい場合は、theme.json でスタイルを定義するか、add_editor_style() でエディターにもテーマの CSS を読み込ませます。ブロック単位なら block.json の style(フロントとエディターの両方に読み込まれる)に書くのが確実です。
レイアウトが崩れる・文字が小さすぎる
viewportWidth を見直してください。既定の 700 に対して、プレビューを置く領域が極端に狭いと、縮小率が大きくなって文字が読めなくなります。サイドバーのような狭い場所では 400〜600 程度に下げると、バランスが取りやすくなります。
逆に、PC 表示でのカラム配置を見せたいのに縦積みで表示されるという場合は、viewportWidth が小さすぎてブレークポイントを下回っています。この値を上げると、広い画面での表示として描画されます。
プレビューの中をクリックしても反応しない
仕様どおりの動作です。BlockPreview は表示専用なので、中のブロックを選択したり編集したりはできません。クリックで何かをさせたい場合は、上の例のようにプレビュー全体を Button などで囲み、その外側でイベントを受け取ります。
まとめ
BlockPreview は、@wordpress/block-editor が提供する表示専用のプレビューコンポーネントです。blocks にブロックオブジェクトを渡すだけで、そのブロックの見た目を縮小表示してくれます。
指定できるのは blocks と viewportWidth の2つだけで、viewportWidth は「何 px の画面で見たものとして描くか」を意味します。狭い場所に置くときは値を下げる、PC 表示を見せたいときは上げる、と覚えておけば十分です。
組み合わせ先としては、Placeholder でのレイアウト選択画面が定番です。createBlock() で構成を作り、選ばれたら replaceInnerBlocks() で流し込む、という流れを押さえておけば、コアのパターン選択に近い使い勝手を自分のブロックにも持たせられます。