Skip to content

Conversation

AlessioGr
Copy link
Member

@AlessioGr AlessioGr commented Sep 15, 2025

This PR introduces support for conditionally setting allowable block types via a new field.filterOptions property on the blocks field.

Closes the following feature requests: #5348, #4668 (partly)

Example

fields: [
  {
      name: 'enabledBlocks',
      type: 'text',
      admin: {
        description:
          "Change the value of this field to change the enabled blocks of the blocksWithDynamicFilterOptions field. If it's empty, all blocks are enabled.",
      },
    },
    {
      name: 'blocksWithFilterOptions',
      type: 'blocks',
      filterOptions: ['block1', 'block2'],
      blocks: [
        {
          slug: 'block1',
          fields: [
            {
              type: 'text',
              name: 'block1Text',
            },
          ],
        },
        {
          slug: 'block2',
          fields: [
            {
              type: 'text',
              name: 'block2Text',
            },
          ],
        },
        {
          slug: 'block3',
          fields: [
            {
              type: 'text',
              name: 'block3Text',
            },
          ],
        },
      ],
    },
    {
      name: 'blocksWithDynamicFilterOptions',
      type: 'blocks',
      filterOptions: ({ siblingData: _siblingData, data }) => {
        const siblingData = _siblingData as { enabledBlocks: string }

        if (siblingData?.enabledBlocks !== data?.enabledBlocks) {
          // Just an extra assurance that the field is working as intended
          throw new Error('enabledBlocks and siblingData.enabledBlocks must be identical')
        }
        return siblingData?.enabledBlocks?.length ? [siblingData.enabledBlocks] : true
      },
      blocks: [
        {
          slug: 'block1',
          fields: [
            {
              type: 'text',
              name: 'block1Text',
            },
          ],
        },
        {
          slug: 'block2',
          fields: [
            {
              type: 'text',
              name: 'block2Text',
            },
          ],
        },
        {
          slug: 'block3',
          fields: [
            {
              type: 'text',
              name: 'block3Text',
            },
          ],
        },
      ],
    },
]
Screenshot.2025-09-15.at.17.49.06.mp4

Updated error toast:

Screenshot 2025-09-19 at 11 54 08@2x

Rationale

Why not block.condition?

  • Individual blocks are often reused in multiple contexts, where the logic for when they should be available may differ. It’s more appropriate for the blocks field (typically tied to a single collection) to determine availability.
  • Hiding existing blocks when they no longer satisfy a condition would cause issues - for example, reordering blocks would break or cause block data to disappear. Instead, this implementation ensures consistency by throwing a validation error if a block is no longer allowed. This aligns with the behavior of filterOptions in relationship fields, rather than condition.

Why not call it blocksFilterOptions?

Although the type differs from relationship fields, this property is named filterOptions (and not blocksFilterOptions) for consistency across field types. For example, the Select field also uses filterOptions despite its type being unique.


valid?: boolean
value: T
}
} & Pick<
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using Pick is preferred over re-defining types. That way, we also bring over JSDocs

validate: memoizedValidate,
})

const { clientBlocks, clientBlocksAfterFilter } = useMemo(() => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the same as the previous clientBlocks = useMemo(), but it also filters blocks now according to blocksFilterOptions returned from the form state.

We need both the filtered and unfiltered client blocks. The filtered ones are only used for the drawer. We still need the ability to display existing blocks that no longer match the filter and fail validation.

blockReferences?: (Block | BlockSlug)[]
blocks: Block[]
defaultValue?: DefaultValue
filterOptions?: BlocksFilterOptions
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reason for keeping name filterOptions despite different type is in PR description

Copy link
Contributor

github-actions bot commented Sep 15, 2025

📦 esbuild Bundle Analysis for payload

This analysis was generated by esbuild-bundle-analyzer. 🤖

Meta File Out File Size (raw) Note
packages/next/meta_index.json esbuild/index.js 753.05 KB ✅ No change
packages/payload/meta_index.json esbuild/index.js 1.22 MB ⚠️ +2.92 KB (+0.2%)
packages/payload/meta_shared.json esbuild/exports/shared.js 162.13 KB ⚠️ +694 B (+0.4%)
packages/richtext-lexical/meta_client.json esbuild/exports/client_optimized/index.js 259.69 KB ✅ No change
packages/ui/meta_client.json esbuild/exports/client_optimized/index.js 1.14 MB ⚠️ +369 B (+0.0%)
packages/ui/meta_shared.json esbuild/exports/shared_optimized/index.js 14.20 KB ✅ No change
Largest paths These visualization shows top 20 largest paths in the bundle.

Meta file: packages/next/meta_index.json, Out file: esbuild/index.js

Path Size
../../node_modules ${{\color{Goldenrod}{ ████████████████████ }}}$ 80.4%, 601.84 KB
dist/views/Version ${{\color{Goldenrod}{ █▋ }}}$ 6.6%, 49.56 KB
dist/views/Document ${{\color{Goldenrod}{ ▌ }}}$ 2.0%, 15.10 KB
dist/views/List ${{\color{Goldenrod}{ ▎ }}}$ 1.3%, 9.73 KB
dist/views/Root ${{\color{Goldenrod}{ ▎ }}}$ 1.1%, 8.24 KB
dist/views/API ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 5.98 KB
dist/views/Versions ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 5.96 KB
dist/views/Account ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 5.32 KB
dist/elements/DocumentHeader ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 4.67 KB
dist/elements/Nav ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 4.67 KB
dist/views/Login ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 4.39 KB
dist/views/Dashboard ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 3.68 KB
dist/layouts/Root ${{\color{Goldenrod}{ }}}$ 0.4%, 3.11 KB
dist/views/ForgotPassword ${{\color{Goldenrod}{ }}}$ 0.4%, 3.09 KB
dist/templates/Default ${{\color{Goldenrod}{ }}}$ 0.4%, 2.83 KB
dist/views/CreateFirstUser ${{\color{Goldenrod}{ }}}$ 0.4%, 2.76 KB
dist/views/BrowseByFolder ${{\color{Goldenrod}{ }}}$ 0.3%, 2.60 KB
dist/views/CollectionFolders ${{\color{Goldenrod}{ }}}$ 0.3%, 2.46 KB
dist/views/ResetPassword ${{\color{Goldenrod}{ }}}$ 0.3%, 2.41 KB
dist/views/Logout ${{\color{Goldenrod}{ }}}$ 0.3%, 1.91 KB
(other) ${{\color{Goldenrod}{ ████▉ }}}$ 19.6%, 146.53 KB

Meta file: packages/payload/meta_index.json, Out file: esbuild/index.js

Path Size
../../node_modules ${{\color{Goldenrod}{ █████████████████▎ }}}$ 69.3%, 841.02 KB
dist/fields/hooks ${{\color{Goldenrod}{ ▉ }}}$ 3.5%, 41.86 KB
dist/collections/operations ${{\color{Goldenrod}{ ▊ }}}$ 3.0%, 36.21 KB
dist/auth/operations ${{\color{Goldenrod}{ ▎ }}}$ 1.3%, 15.30 KB
dist/queues/operations ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 12.04 KB
dist/globals/operations ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 11.91 KB
dist/fields/config ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 11.57 KB
dist/utilities/configToJSONSchema.js ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 11.54 KB
dist/fields/validations.js ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 10.04 KB
dist/bin/generateImportMap ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 8.27 KB
dist/database/migrations ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.79 KB
dist/collections/config ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.79 KB
dist/uploads/fetchAPI-multipart ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.74 KB
dist/index.js ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.42 KB
dist/collections/endpoints ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.40 KB
dist/config/orderable ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 6.25 KB
dist/auth/strategies ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 5.48 KB
dist/config/sanitize.js ${{\color{Goldenrod}{ }}}$ 0.4%, 5.44 KB
dist/auth/endpoints ${{\color{Goldenrod}{ }}}$ 0.4%, 5.41 KB
dist/utilities/telemetry ${{\color{Goldenrod}{ }}}$ 0.4%, 5.31 KB
(other) ${{\color{Goldenrod}{ ███████▋ }}}$ 30.7%, 371.95 KB

Meta file: packages/payload/meta_shared.json, Out file: esbuild/exports/shared.js

Path Size
../../node_modules ${{\color{Goldenrod}{ ███████████████████▉ }}}$ 79.9%, 126.93 KB
dist/fields/validations.js ${{\color{Goldenrod}{ █▌ }}}$ 6.3%, 10.04 KB
dist/fields/baseFields ${{\color{Goldenrod}{ ▍ }}}$ 1.8%, 2.79 KB
dist/utilities/deepCopyObject.js ${{\color{Goldenrod}{ ▍ }}}$ 1.6%, 2.48 KB
dist/auth/cookies.js ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 1.55 KB
dist/utilities/flattenTopLevelFields.js ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 1.42 KB
dist/fields/config ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 1.28 KB
dist/utilities/flattenAllFields.js ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 943 B
dist/folders/utils ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 916 B
dist/utilities/unflatten.js ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 779 B
dist/utilities/sanitizeUserDataForEmail.js ${{\color{Goldenrod}{ }}}$ 0.4%, 713 B
dist/collections/config ${{\color{Goldenrod}{ }}}$ 0.4%, 570 B
dist/bin/generateImportMap ${{\color{Goldenrod}{ }}}$ 0.4%, 559 B
dist/auth/sessions.js ${{\color{Goldenrod}{ }}}$ 0.3%, 545 B
dist/utilities/getSafeRedirect.js ${{\color{Goldenrod}{ }}}$ 0.3%, 423 B
dist/utilities/deepMerge.js ${{\color{Goldenrod}{ }}}$ 0.3%, 413 B
dist/utilities/getFieldPermissions.js ${{\color{Goldenrod}{ }}}$ 0.2%, 391 B
dist/utilities/formatLabels.js ${{\color{Goldenrod}{ }}}$ 0.2%, 380 B
dist/utilities/appendUploadSelectFields.js ${{\color{Goldenrod}{ }}}$ 0.2%, 360 B
dist/utilities/transformColumnPreferences.js ${{\color{Goldenrod}{ }}}$ 0.2%, 348 B
(other) ${{\color{Goldenrod}{ █████ }}}$ 20.1%, 31.93 KB

Meta file: packages/richtext-lexical/meta_client.json, Out file: esbuild/exports/client_optimized/index.js

Path Size
dist/lexical/plugins ${{\color{Goldenrod}{ ██▉ }}}$ 11.8%, 30.17 KB
dist/features/experimental_table ${{\color{Goldenrod}{ ██▎ }}}$ 9.2%, 23.66 KB
dist/lexical/ui ${{\color{Goldenrod}{ ██▎ }}}$ 9.1%, 23.36 KB
dist/features/blocks ${{\color{Goldenrod}{ ██▏ }}}$ 8.7%, 22.41 KB
dist/packages/@lexical ${{\color{Goldenrod}{ █▊ }}}$ 7.4%, 18.99 KB
dist/features/link ${{\color{Goldenrod}{ █▊ }}}$ 7.0%, 17.96 KB
dist/features/toolbars ${{\color{Goldenrod}{ █▋ }}}$ 6.9%, 17.59 KB
dist/features/textState ${{\color{Goldenrod}{ █ }}}$ 4.3%, 11.02 KB
dist/features/upload ${{\color{Goldenrod}{ ▉ }}}$ 3.7%, 9.60 KB
dist/features/relationship ${{\color{Goldenrod}{ ▉ }}}$ 3.5%, 9.09 KB
dist/lexical/utils ${{\color{Goldenrod}{ ▊ }}}$ 3.1%, 8.08 KB
dist/features/debug ${{\color{Goldenrod}{ ▋ }}}$ 2.9%, 7.39 KB
dist/features/converters ${{\color{Goldenrod}{ ▋ }}}$ 2.7%, 7.04 KB
dist/utilities/fieldsDrawer ${{\color{Goldenrod}{ ▋ }}}$ 2.7%, 7.01 KB
dist/lexical/config ${{\color{Goldenrod}{ ▌ }}}$ 2.0%, 5.10 KB
dist/features/lists ${{\color{Goldenrod}{ ▍ }}}$ 1.9%, 4.95 KB
dist/lexical/theme ${{\color{Goldenrod}{ ▍ }}}$ 1.6%, 4.01 KB
dist/features/format ${{\color{Goldenrod}{ ▎ }}}$ 1.3%, 3.46 KB
dist/lexical/LexicalEditor.js ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 3.17 KB
dist/features/indent ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 2.50 KB
(other) ${{\color{Goldenrod}{ ██████████████████████ }}}$ 88.2%, 226.45 KB

Meta file: packages/ui/meta_client.json, Out file: esbuild/exports/client_optimized/index.js

Path Size
../../node_modules ${{\color{Goldenrod}{ ████████████▋ }}}$ 50.8%, 572.91 KB
dist/elements/FolderView ${{\color{Goldenrod}{ ▋ }}}$ 2.6%, 29.14 KB
dist/elements/BulkUpload ${{\color{Goldenrod}{ ▌ }}}$ 2.2%, 24.70 KB
dist/elements/WhereBuilder ${{\color{Goldenrod}{ ▎ }}}$ 1.4%, 16.16 KB
dist/fields/Relationship ${{\color{Goldenrod}{ ▎ }}}$ 1.4%, 15.41 KB
dist/elements/Table ${{\color{Goldenrod}{ ▎ }}}$ 1.4%, 15.25 KB
dist/views/Edit ${{\color{Goldenrod}{ ▎ }}}$ 1.3%, 15.22 KB
dist/forms/Form ${{\color{Goldenrod}{ ▎ }}}$ 1.3%, 14.99 KB
dist/fields/Blocks ${{\color{Goldenrod}{ ▎ }}}$ 1.1%, 12.64 KB
dist/fields/Upload ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 11.46 KB
dist/elements/PublishButton ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 8.73 KB
dist/providers/Folders ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 8.49 KB
dist/elements/QueryPresets ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 8.27 KB
dist/elements/ListHeader ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 7.83 KB
dist/elements/HTMLDiff ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 7.81 KB
dist/fields/Array ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 7.54 KB
dist/views/CollectionFolder ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 7.36 KB
dist/elements/LivePreview ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.19 KB
dist/views/List ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 6.96 KB
dist/elements/ReactSelect ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 6.91 KB
(other) ${{\color{Goldenrod}{ ████████████▎ }}}$ 49.2%, 555.19 KB

Meta file: packages/ui/meta_shared.json, Out file: esbuild/exports/shared_optimized/index.js

Path Size
dist/graphics/Logo ${{\color{Goldenrod}{ █████▋ }}}$ 22.9%, 3.12 KB
../../node_modules ${{\color{Goldenrod}{ ████▉ }}}$ 19.5%, 2.65 KB
dist/graphics/Icon ${{\color{Goldenrod}{ ██▊ }}}$ 11.2%, 1.52 KB
dist/utilities/formatDocTitle ${{\color{Goldenrod}{ ██▍ }}}$ 9.7%, 1.32 KB
dist/providers/TableColumns ${{\color{Goldenrod}{ █▌ }}}$ 6.3%, 862 B
dist/utilities/groupNavItems.js ${{\color{Goldenrod}{ █▌ }}}$ 6.0%, 814 B
dist/utilities/api.js ${{\color{Goldenrod}{ █▍ }}}$ 5.6%, 756 B
dist/elements/Translation ${{\color{Goldenrod}{ ▉ }}}$ 3.6%, 493 B
dist/elements/withMergedProps ${{\color{Goldenrod}{ ▋ }}}$ 2.5%, 339 B
dist/utilities/handleTakeOver.js ${{\color{Goldenrod}{ ▍ }}}$ 1.8%, 251 B
dist/elements/WithServerSideProps ${{\color{Goldenrod}{ ▍ }}}$ 1.7%, 232 B
dist/utilities/handleGoBack.js ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 168 B
dist/fields/mergeFieldStyles.js ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 159 B
dist/forms/Form ${{\color{Goldenrod}{ ▎ }}}$ 1.1%, 147 B
dist/utilities/abortAndIgnore.js ${{\color{Goldenrod}{ ▎ }}}$ 1.1%, 146 B
dist/utilities/hasSavePermission.js ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 136 B
dist/utilities/handleBackToDashboard.js ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 129 B
dist/utilities/findLocaleFromCode.js ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 84 B
dist/utilities/sanitizeID.js ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 77 B
dist/utilities/isEditing.js ${{\color{Goldenrod}{ }}}$ 0.4%, 59 B
(other) ${{\color{Goldenrod}{ ███████████████████▎ }}}$ 77.1%, 10.49 KB
Details

Next to the size is how much the size has increased or decreased compared with the base branch of this PR.

  • ‼️: Size increased by 20% or more. Special attention should be given to this.
  • ⚠️: Size increased in acceptable range (lower than 20%).
  • ✅: No change or even downsized.
  • 🗑️: The out file is deleted: not found in base branch.
  • 🆕: The out file is newly found: will be added to base branch.

@AlessioGr AlessioGr marked this pull request as ready for review September 15, 2025 04:29
* Undefined values for blocksFilterOptions coming back should be treated as "all blocks allowed" and
* should always be merged in.
*/
newState[path].blocksFilterOptions = incomingField.blocksFilterOptions
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without this, if blocksFilterOptions goes from a value to undefined, it would be ignored

/**
* @deprecated - readOnly is no longer returned from useField. Remove this in 4.0.
*/
readOnly?: boolean
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

readOnly currently is always undefined. I think it's only there to prevent a breaking change (narrower type, project won't build)

@AlessioGr AlessioGr enabled auto-merge (squash) September 15, 2025 05:10
@AlessioGr AlessioGr requested a review from paulpopus September 15, 2025 16:33
@jmikrut
Copy link
Member

jmikrut commented Sep 15, 2025

image

The error state could be clearer - The following field has invalid selections: - this needs a bit of work.

Could we show the errored rows in red?

At the very least, could we write a more descriptive error? As-is, the user has no easy way to identify the problematic block. Maybe This field contains CallToAction and Accordion block(s) that are invalid.... or similar?

Note: when we call .filterOptions() for blocks in form state thats when we can add more detail to error paths to make block red

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants