A disabled field is visible but genuinely unchangeable — greyed out, unclickable, and critically, its submitted value comes from #value (or #default_value), never from anything a user could have typed, because they couldn't type anything at all. It's the right tool whenever a field needs to show information without letting anyone alter it.
What you'll learn in this lesson
- #disabled's visual and functional effect together
- Why a disabled field's value is genuinely tamper-proof, unlike hidden
- Conditionally disabling based on permissions
A disabled field
$form['account_id'] = [
'#type' => 'textfield',
'#title' => $this->t('Account ID'),
'#default_value' => $account_id,
'#disabled' => TRUE,
];
Renders visibly, greyed out, genuinely unfocusable and unchangeable in the browser — a real HTML disabled attribute, not just visual styling pretending to be disabled.
#disabled field is different: Drupal's Form API server-side deliberately IGNORES whatever value a tampered request might claim for a disabled field, and uses the value from #default_value (or #value) instead — regardless of what the raw POST data actually contained. This makes #disabled the right choice specifically when a value must be shown but must never be attacker-controllable, stronger than hidden for that purpose.Conditionally disabling based on permission
$form['status'] = [
'#type' => 'select',
'#title' => $this->t('Status'),
'#options' => $status_options,
'#default_value' => $current_status,
'#disabled' => !$this->currentUser()->hasPermission('change status'),
];
This is a common, genuinely useful pattern: the same form serves both users who can and can't change a specific field — those without permission see the current value but can't touch it, rather than needing an entirely separate form variant per permission level.
Quick check: a disabled field shows "Pending" as its value. A user crafts a raw HTTP POST request setting that field's submitted value to "Approved" directly, bypassing the browser UI entirely. Does the tampered value make it through? If you said no — Drupal's server-side Form API ignores submitted values for disabled fields regardless of what the raw request contains, using the server-set default instead — you've understood exactly why #disabled is stronger than a hidden field for this purpose.
Key takeaways
- #disabled renders a real, unfocusable HTML disabled attribute — genuinely unchangeable, not just styled to look that way.
- Drupal's server-side Form API ignores tampered submitted values for disabled fields entirely, using the server-set default instead — stronger than a hidden field for security-sensitive display-only values.
- Conditionally disabling based on permission lets one form serve multiple access levels without duplicating the form class.
Coming up next
Disabled shows a field without allowing changes. Next, hiding a field entirely with #access.