Altering Entity Forms with hook_entity_form_alter()for Drupal 11 , 10 , 9 , and 8

Last updated :  

Everything in this section so far changed one entity form by editing its own class or config. But what if you need to reach into every entity form of a given type across the whole site — including ones defined by contributed modules you'll never edit? That's exactly what hook_entity_form_alter() is for, and it's the entity-form equivalent of hook_form_alter() from earlier in this tutorial.

What you'll learn in this lesson

  • How hook_entity_form_alter() differs from the generic hook_form_alter()
  • How to check which entity type and bundle you're altering before making changes
  • How to reach the entity object itself from inside the alter hook
  • When to reach for this hook instead of a form-display config change or a class override

The hook, in shape

/**
 * Implements hook_entity_form_alter().
 */
function my_module_entity_form_alter(array &$form, FormStateInterface $form_state, $form_id) {
  $form_object = $form_state->getFormObject();
  if (!$form_object instanceof \Drupal\Core\Entity\EntityFormInterface) {
    return;
  }

  $entity = $form_object->getEntity();
  if ($entity->getEntityTypeId() !== 'content_entity_example_contact') {
    return;
  }

  $form['name']['widget'][0]['value']['#description'] = t('Enter the contact\'s full legal name.');
}

Two guard clauses matter here. First, confirming the form object actually implements EntityFormInterface — hook_entity_form_alter() only fires for entity forms, but it's still good practice to check before calling entity-specific methods on it. Second, checking the entity type ID, since this hook fires for every entity form on the site, not just the one you care about.

Why not just use hook_form_FORM_ID_alter() instead?

You could — Drupal auto-generates a form ID for entity forms too (something like content_entity_example_contact_add_form), so hook_form_content_entity_example_contact_add_form_alter() would work for targeting exactly that one form. The advantage of hook_entity_form_alter() is reaching every operation (add and edit) for an entity type with one implementation, and getting direct access to $entity without having to reconstruct it from raw form values yourself.

This hook fires for every entity form, site-wide: that includes node forms, taxonomy term forms, user forms — anything built on EntityFormInterface. The entity-type check above isn't optional defensive code; without it, your alter logic silently runs against forms you never intended to touch.

See it for yourself

Enable a small custom module implementing the hook above (or add it temporarily to an existing one), clear cache, and visit the contact add form — the Name field's description text should now show your altered version.

Quick check: you want your alter logic to apply only to the add operation, never edit. What's the cleanest addition to the guard clauses above? (Check $entity->isNew() — the same check used earlier in this section to distinguish add from edit inside a form class works identically here, since $entity is available either way.)

Key takeaways

  • hook_entity_form_alter() fires for every entity form on the site — always guard with an entity-type (and often bundle) check before altering.
  • Get the entity itself via $form_state->getFormObject()->getEntity(), after confirming the form object implements EntityFormInterface.
  • Prefer this hook over a class override when the change needs to reach every operation (add and edit) or every module that defines forms for that entity type.
  • Combine with $entity->isNew() when you need add-only or edit-only behavior from inside the alter hook.

Coming up next

You've now covered the full lifecycle of entity forms — building, validating, saving, configuring their display, and altering them site-wide. Next, we shift focus to how forms actually render visually: theming form elements.