Drupal 10 Layout Builder inline block form custom validation callback not firing

9 hours ago 1
ARTICLE AD BOX

I am trying to add custom validation for a custom inline block type in Drupal 10 Layout Builder.

The UI logic (#states) works correctly, but my custom #validate callback never executes inside the Layout Builder modal form.

I have already spent a lot of time debugging this and would appreciate guidance on the correct Drupal approach for validating inline block forms in Layout Builder.

Context:

Custom block type:

v2_banner

Requirements:

If field_v2_is_fully_clickable = TRUE

field_v2_link is required

If field_v2_is_fully_clickable = FALSE

field_v2_buttons_x2 is required

field_v2_title OR body required

Initially I implemented this using Entity Constraints + Constraint Validators, but Layout Builder AJAX forms crashed with:

Undefined array key "#parents"

because of ->atPath() usage during AJAX rebuilds.

So I moved validation into Form API validation.
Hooks in .module

function my_custom_blocks_v2_banner_form_block_content_form_alter( &$form, FormStateInterface $form_state, $form_id ) { \Drupal::service(FormAlterHooks::class) ->bannerBlockFormAlter($form, $form_state, $form_id); } function my_custom_blocks_v2_banner_form_layout_builder_add_block_alter( &$form, FormStateInterface $form_state, $form_id ) { \Drupal::service(FormAlterHooks::class) ->bannerBlockFormAlter($form, $form_state, $form_id); } function my_custom_blocks_v2_banner_form_layout_builder_update_block_alter( &$form, FormStateInterface $form_state, $form_id ) { \Drupal::service(FormAlterHooks::class) ->bannerBlockFormAlter($form, $form_state, $form_id); }
final class FormAlterHooks { private function getFormEntityType(array $form, FormStateInterface $form_state): ?string { $form_object = $form_state->getFormObject(); if ($form_object instanceof EntityFormInterface) { $entity = $form_object->getEntity(); return "{$entity->getEntityType()->id()}:{$entity->bundle()}"; } elseif ($form_object instanceof LayoutBuilderForm) { if (empty($form['#block'])) { return NULL; } $block = $form['#block']; return "{$block->getEntityType()->id()}:{$block->bundle()}"; } return NULL; } public function bannerBlockFormAlter( &$form, FormStateInterface $form_state, $form_id ): void { if ($this->getFormEntityType($form, $form_state) !== 'block_content:v2_banner') { return; } // Validation callback. if (!empty($form['settings']['block_form'])) { $form['settings']['block_form']['#validate'][] = [ self::class, 'validateBannerForm' ]; } else { $form['#validate'][] = [ self::class, 'validateBannerForm' ]; } // States logic works correctly. $form['field_v2_link']['#states']['visible'] = [ ':input[name="field_v2_is_fully_clickable[value]"]' => ['checked' => TRUE], ]; $form['field_v2_buttons_x2']['#states']['visible'] = [ ':input[name="field_v2_is_fully_clickable[value]"]' => ['checked' => FALSE], ]; } public static function validateBannerForm( array &$form, FormStateInterface $form_state ): void { throw new \Exception('VALIDATION RUNNING'); } } hook_form_layout_builder_add_block_alter() executes hook_form_layout_builder_update_block_alter() executes #states logic works correctly form alter definitely runs Layout Builder modal loads correctly

The validation callback never executes.

Even this never triggers:

throw new \Exception('VALIDATION RUNNING');

No exception appears anywhere:

no AJAX error no logs no exception page

I also previously tried:

Entity Constraints Constraint Validators ->atPath() $form['#entity_builders'] $form['#element_validate']

but Layout Builder AJAX forms became unstable.

What is the correct Drupal 10 approach for attaching custom validation to Layout Builder inline block forms? Any guidance or examples would be appreciated.

Read Entire Article