🙌

Payloadのブロックフィールドの使い方

に公開

はじめに

Payloadには、ブロックフィールドという特殊なフィールドがあります。テキストフィールドやチェックボックスフィールドのようなシンプルなフィールドとは異なり、ブロックフィールドは少し複雑です。

この記事では、ブロックフィールドの使い方について、ブロックフィールドの定義方法やフロント画面の実装方法を交えて解説したいと思います。

前提

  • ブロックフィールドを使用するには必要なコード量が多いため、Payloadのwebsiteテンプレートを使用している前提で解説します
  • 管理画面のサポート言語に日本語と英語を設定しています

管理画面の言語設定については、下記の記事をご参照ください。

https://zenn.dev/ksk1kd/articles/b69038ec67f799

ブロックフィールドの概要

ブロックフィールドとは、Webページをブロックというパーツの組み合わせで作成するための機能です。

たとえば、下記のブロックを用意したとします。

  • スライダー
  • 見出し
  • リッチテキスト
  • メディア
  • アコーディオン

そうすると、Webサイトのトップページや各種下層ページをこれらのブロックの組み合わせで作成することができます。

ブロックフィールドの管理画面でのUIは、下記キャプチャのようになります。

ブロックフィールドの管理画面でのUI

「Add Layout」をクリックすると、下記キャプチャのモーダルが表示され、あらかじめ定義したブロックの一覧が表示されます。

ブロック一覧のモーダル

使用したいブロックを選択すると先ほどの画面に戻り、選択したブロックがブロックフィールドに追加されます。

ブロックを追加済みのブロックフィールド

Webページの構成に必要なブロックを次々に追加していくことができます。

複数のブロックを追加済みのブロックフィールド

これにより、簡単に好みのレイアウトでWebページを作成できます。また、追加したブロックは後からドラッグ&ドロップで順番を入れ替えたり、特定のブロックのみを削除できたりします。

ブロックの定義方法

ブロックの定義方法について解説したいと思います。

この記事では例として、「テキスト + メディア」というブロックを定義してみます。「テキスト + メディア」ブロックは、テキストとメディアを横並びの2カラムで表示するためのブロックです。左にテキスト・右にメディアのパターンと、左にメディア・右にテキストのパターンに対応したいと思います。

上記の仕様を実現するために、「テキスト + メディア」ブロックには下記のフィールドを設けます。

  • テキスト
  • メディア
  • メディアの位置(左か右かを選択するラジオボタン)

以上を踏まえてブロックを定義していきます。

まず、「テキスト + メディア」ブロックを定義するにはsrc/blocks/TextAndMediaBlock/config.tsを作成し、下記のコードを記述します。

src/blocks/TextAndMediaBlock/config.ts
import type { Block } from 'payload'

export const TextAndMediaBlock: Block = {
  slug: 'textAndMediaBlock',
  labels: {
    singular: {
      en: 'Text + Media Block',
      ja: 'テキスト + メディア ブロック',
    },
    plural: {
      en: 'Text + Media Block',
      ja: 'テキスト + メディア ブロック',
    },
  },
  interfaceName: 'TextAndMediaBlock',
  fields: [
    {
      name: 'text',
      label: {
        en: 'Text',
        ja: 'テキスト',
      },
      type: 'richText',
      required: true,
    },
    {
      name: 'media',
      label: {
        en: 'Media',
        ja: 'メディア',
      },
      type: 'upload',
      relationTo: 'media',
      required: true,
    },
    {
      name: 'mediaPostion',
      label: {
        en: 'Media Postion',
        ja: 'メディアの位置',
      },
      type: 'radio',
      options: [
        {
          label: {
            en: 'Left',
            ja: '左'
          },
          value: 'left',
        },
        {
          label: {
            en: 'Right',
            ja: '右',
          },
          value: 'right',
        }
      ],
      required: true,
      defaultValue: 'left',
    }
  ],
}

次に、websiteテンプレートにあるPagesコレクションのブロックフィールドに、上記で定義した「テキスト + メディア」ブロックを追加します。

src/collections/Pages/index.ts
import { TextAndMediaBlock } from '../../blocks/TextAndMediaBlock/config'

// ...
export const Pages: CollectionConfig<'pages'> = {
          // ...
          fields: [
            {
              name: 'layout',
              type: 'blocks',
              blocks: [CallToAction, Content, MediaBlock, Archive, FormBlock, TextAndMediaBlock],
              required: true,
              admin: {
                initCollapsed: true,
              },
            },
          ],
          label: 'Content',
          // ...
}

以上の対応で、追加した「テキスト + メディア」ブロックがブロック一覧に表示され、使用できるようになります。

「テキスト + メディア」ブロックがブロック一覧に表示されている

フロント画面の実装

「テキスト + メディア」ブロックをフロント画面に表示させる実装方法について解説したいと思います。

まず、src/blocks/TextAndMediaBlock/Component.tsxを作成し、下記のコードを記述します。これは「テキスト + メディア」ブロックを表示するためのコンポーネントです。mediaPositionの値に応じて、テキストとメディアの表示位置が切り替わるよう実装しています。

src/blocks/TextAndMediaBlock/Component.tsx
import React from 'react'
import type { TextAndMediaBlock as TextAndMediaBlockProps } from '@/payload-types'
import RichText from '@/components/RichText'
import { Media } from '@/components/Media'


export const TextAndMediaBlock: React.FC<TextAndMediaBlockProps> = (props) => {
  const {
    text,
    media,
    mediaPostion
  } = props

  const mediaComponent = media && <Media resource={media} />
  const textComponent = text && <RichText data={text} enableGutter={false} className="w-full" />

  return (
    <div className="container grid grid-cols-2 gap-20">
      {mediaPostion === 'left' ? (
        <>
          {mediaComponent}
          {textComponent}
        </>
      ) : (
        <>
          {textComponent}
          {mediaComponent}
        </>
      )}
    </div>
  )
}

次に、src/blocks/RenderBlocks.tsxに、上記で作成したコンポーネントを追加します。

src/blocks/RenderBlocks.tsx
import { TextAndMediaBlock } from '@/blocks/TextAndMediaBlock/Component'

const blockComponents = {
  archive: ArchiveBlock,
  content: ContentBlock,
  cta: CallToActionBlock,
  formBlock: FormBlock,
  mediaBlock: MediaBlock,
  textAndMediaBlock: TextAndMediaBlock,
}

// ...

以上で完了です。

動作確認

最後に動作確認をします。

まず、下記の設定でページを作成し、左にメディア・右にテキストのパターンを確認します。

左にメディア・右にテキストのパターンを確認するための設定

問題なく左にメディア・右にテキストが表示されることを確認できました。

左にメディア・右にテキストが表示されているフロント画面

次に、下記の設定でページを作成し、左にテキスト・右にメディアのパターンを確認します。

左にテキスト・右にメディアのパターンを確認するための設定

問題なく左にテキスト・右にメディアが表示されることを確認できました。

左にテキスト・右にメディアが表示されているフロント画面

まとめ

この記事では、ブロックフィールドの使い方について、ブロックフィールドの定義方法やフロント画面の実装方法を交えて解説しました。ブロックフィールドを使用することで、簡単に好みのレイアウトでWebページを作成できることを理解していただけたかと思います。
ただし、ブロックの定義追加やフロント画面の実装は結構手間がかかります。また、実際の開発ではどのようなブロックを用意するかといった要件定義や設計にも多大な時間を要することになります。本当にブロックフィールドが必要かは慎重に検討されることをおすすめします。

参考

https://payloadcms.com/docs/fields/blocks

Discussion