When to Use
Use AJAX when you need server-side logic or dynamic options. Use #states for simple show/hide (faster, client-side only).
Decision
| Situation |
Choose |
Why |
| Dynamic options based on input |
AJAX |
Need server data |
| Dependent dropdowns |
AJAX |
Options must change |
| Load entities/data |
AJAX |
Server-side processing |
| Simple show/hide fields |
#states |
Faster, no server call |
| Field enable/disable |
#states |
Client-side only |
Pattern
// Trigger element with AJAX
$form['country'] = [
'#type' => 'select',
'#title' => $this->t('Country'),
'#options' => $countries,
'#ajax' => [
'callback' => '::updateStates',
'wrapper' => 'states-wrapper',
'event' => 'change', // Default for select
],
];
// Target container
$form['states_wrapper'] = [
'#type' => 'container',
'#attributes' => ['id' => 'states-wrapper'],
];
$form['states_wrapper']['state'] = [
'#type' => 'select',
'#title' => $this->t('State'),
'#options' => $this->getStates($form_state),
];
// Callback returns element
public function updateStates(array &$form, FormStateInterface $form_state) {
return $form['states_wrapper'];
}
AJAX Property Configuration
'#ajax' => [
'callback' => '::ajaxCallback', // Required
'wrapper' => 'element-id', // Target container ID
'event' => 'change', // Trigger event
'method' => 'replace', // Command method
'effect' => 'fade', // Visual effect
'progress' => [
'type' => 'throbber', // or 'fullscreen'
'message' => $this->t('Loading...'),
],
],
Common Events
| Element Type |
Default Event |
Alternative |
| Select, radio, checkbox |
change |
- |
| Button, link |
click |
- |
| Textfield |
change |
keyup (use debounce) |
| Any |
blur |
custom |
Advanced Pattern: AjaxResponse
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\Ajax\MessageCommand;
public function ajaxCallback(array &$form, FormStateInterface $form_state) {
$response = new AjaxResponse();
$response->addCommand(new ReplaceCommand('#wrapper', $form['element']));
$response->addCommand(new MessageCommand('Updated'));
return $response;
}
Core AJAX Commands
| Command |
Purpose |
Parameters |
| ReplaceCommand |
Replace element |
selector, content |
| AppendCommand |
Add after |
selector, content |
| PrependCommand |
Add before |
selector, content |
| RemoveCommand |
Delete element |
selector |
| MessageCommand |
Show status |
message, type |
| RedirectCommand |
Navigate |
url |
| InvokeCommand |
Call JS method |
selector, method, args |
| OpenModalDialogCommand |
Open modal |
title, content, options |
Common Mistakes
- Wrong: Missing wrapper ID in #ajax → Right: Always specify wrapper
- Wrong: Wrapper ID doesn't exist → Right: Check HTML output
- Wrong: Not returning render array or AjaxResponse → Right: Return one or the other
- Wrong: Using non-unique wrapper IDs → Right: Unique IDs prevent wrong replacements
See Also