Think of Form API as Drupal's own form-builder factory: instead of hand-writing HTML <form> tags and manually wiring up validation and submission logic, you describe a form as a plain PHP array, and Drupal's Form API assembles the actual HTML, handles CSRF protection, manages multi-step state, and routes submitted values back to your code — all from that one array description.
What you'll learn in this lesson
- Why Form API exists instead of just writing raw HTML forms
- The three-method contract every form class implements
- What Form API handles for you automatically, invisibly
Why not just write HTML?
A hand-written HTML form has to separately handle: CSRF token generation and validation, preserving submitted values on a validation error, multi-step state across requests, AJAX partial updates, and consistent theming across every form on the site. Form API solves all of this once, centrally — your job becomes describing what fields exist, not re-solving each of those problems for every form you write.
The three-method contract
namespace Drupal\form_api_example\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
class SimpleForm extends FormBase {
public function getFormId() {
return 'form_api_example_simple_form';
}
public function buildForm(array $form, FormStateInterface $form_state) {
$form['name'] = [
'#type' => 'textfield',
'#title' => $this->t('Your name'),
];
$form['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Submit'),
];
return $form;
}
public function submitForm(array &$form, FormStateInterface $form_state) {
$this->messenger()->addMessage($this->t('Hello, @name', [
'@name' => $form_state->getValue('name'),
]));
}
}
This is the real, minimal shape of a Form API form, taken directly from the Examples module's SimpleForm.php. Three methods, each with one job: getFormId() gives the form a unique machine name, buildForm() returns the array describing every field, and submitForm() runs after a successful submission.
buildForm() gets a hidden CSRF token field, a form_id hidden field, and form-build-id tracking automatically added by Drupal before rendering — none of which you wrote, all of which protects against cross-site request forgery and lets Drupal reconstruct exactly which form and which build submitted, even across an AJAX round trip.Quick check: why does a Form API form need a
getFormId()method at all — why can't Drupal just use the PHP class name? If you said because the form ID is what appears in HTML (in the hiddenform_idfield) and what other code targets with hooks likehook_form_FORM_ID_alter(), and a class name isn't guaranteed unique or stable across refactors the same way, you've understood the real reason it's explicit.
Key takeaways
- Form API describes a form as a PHP array — Drupal handles HTML generation, CSRF protection, and state management from that description.
- Every form class implements
getFormId(),buildForm(), andsubmitForm()at minimum. - CSRF tokens and form-build tracking get added automatically — you never write that part yourself.
- The form ID (not the class name) is what hooks and other code target to identify a specific form.
Coming up next
You've seen the shape of a complete form. Next, we build one from scratch, starting with the base class every form extends.