Not every alter is about logic — sometimes you just need a field to look different: a custom CSS class, a placeholder, a different title, or a data attribute your JavaScript hooks into. This lesson closes out the Form Alterations topic with the smallest, most common kind of change: reaching into an element's #attributes and #title properties from outside the form's own code.
What you'll learn in this lesson
- How to add or change an element's
#attributeswithout destroying attributes already set there - How to change
#title,#placeholder, and other simple display properties - Why merging attributes matters more than it looks like it should
The pattern
/**
* Implements hook_form_FORM_ID_alter() for user_login_form.
*/
function mymodule_form_user_login_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state) {
// Change the title.
$form['name']['#title'] = t('Email or username');
// Add a placeholder.
$form['name']['#attributes']['placeholder'] = t('you@example.com');
// Add a CSS class without erasing any classes already there.
$form['name']['#attributes']['class'][] = 'mymodule-highlighted-field';
}
How it works
#title and #placeholder are simple overwrites
$form['name']['#title'] = t('Email or username');
These are scalar string properties, so setting them directly is safe — there's nothing to accidentally destroy, since the property held exactly one value before your change and holds exactly one value after it.
Why class needs [] =, not =
$form['name']['#attributes']['class'][] = 'mymodule-highlighted-field';
class attribute is itself an array of class names, not a single string. Writing $form['name']['#attributes']['class'] = ['mymodule-highlighted-field'] (with a bare =) would silently wipe out every CSS class the element already had — including ones core or the theme depends on for styling or JavaScript behavior to work correctly. Appending with [] = adds your class alongside whatever's already there.The same rule applies to any array-valued attribute
This isn't specific to class — any attribute that Drupal or another module might also be writing to (like data-drupal-selector-adjacent custom data attributes some JS libraries expect as arrays) deserves the same caution: check whether the key already holds a value before deciding whether to overwrite or append.
See it for yourself
Visit the target form and inspect the element's rendered HTML — confirm the new placeholder attribute appears, the title text changed, and (critically) any CSS classes the element had before your alter hook ran are still present alongside your new one.
Quick check: you want to add a custom class to a text field that Drupal core itself already classes with
form-textfor its default styling. Which line correctly adds your class without losing that one? (Answer:$form['field']['#attributes']['class'][] = 'my-class';— the trailing[]appends rather than replacing the whole array, soform-textsurvives right alongside it.)
Key takeaways
- Scalar properties like
#titleand#placeholdercan be safely overwritten directly — there's nothing to preserve. - Array-valued attributes like
classmust be appended to with[] =, never overwritten with a bare=, or you'll silently erase classes core or the theme depends on. - This same append-don't-overwrite caution applies to any attribute key that other code might already be writing to.
- Always inspect the rendered HTML after an attribute alter to confirm nothing pre-existing was lost.
Coming up next
You've now covered every angle of altering someone else's form — structure, validation, submission, and appearance. Next, we turn to a different kind of form entirely: building your own configuration form from scratch, using ConfigFormBase to let site administrators manage your module's settings.