Last lesson introduced AppendCommand as one way to tell the browser what to do with an AJAX response. Think of Drupal's AJAX commands as a small, fixed vocabulary the server and browser both agree on in advance — the server never sends raw instructions, it sends one of these named commands, and JavaScript already shipped with Drupal core knows exactly how to carry each one out. This lesson tours the rest of that vocabulary.
What you'll learn in this lesson
- The most commonly used AJAX commands and what each one actually does to the page
- How
ReplaceCommanddiffers fromHtmlCommand— a distinction that trips people up - How to open a real modal dialog from an AJAX callback
- Why you'll usually only need three or four of these commands in practice
The core commands you'll actually reach for
| Command | What it does |
|---|---|
HtmlCommand | Replaces the inner HTML of a selector, leaving the element itself in place |
ReplaceCommand | Replaces the selector element itself, including its own tag and attributes |
AppendCommand | Adds new content inside a selector, after whatever's already there |
RemoveCommand | Deletes matching elements from the page entirely |
InvokeCommand | Calls an arbitrary jQuery method on matching elements (e.g. addClass, focus) |
CssCommand | Sets one or more CSS properties directly on matching elements |
MessageCommand | Displays a Drupal status/warning/error message via the messenger system |
HtmlCommand vs. ReplaceCommand — the distinction that trips people up
// Replaces everything INSIDE #my-wrapper, the wrapper <div> itself survives.
$response->addCommand(new HtmlCommand('#my-wrapper', $new_markup));
// Replaces #my-wrapper ITSELF — a brand-new element takes its place.
$response->addCommand(new ReplaceCommand('#my-wrapper', $new_markup));
This matters more than it looks like it should: if your own new markup doesn't itself include an element with id="my-wrapper", a ReplaceCommand will silently remove your only hook for targeting that region again on a future AJAX request. HtmlCommand avoids that trap entirely, which is why it's the safer default whenever you're not certain which one you need.
A real command in action: opening a modal
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\OpenModalDialogCommand;
public function openModal(array &$form, FormStateInterface $form_state) {
$content = ['#markup' => $this->t('This content is dynamically loaded.')];
$response = new AjaxResponse();
$response->addCommand(new OpenModalDialogCommand($this->t('Modal Title'), $content, ['width' => '400']));
return $response;
}
This is real code from the Examples module's ModalForm. Notice it doesn't target a wrapper element at all — OpenModalDialogCommand builds and displays an entirely new dialog on top of the page, which is exactly the kind of thing the render-array shortcut from two lessons ago simply cannot express.
HtmlCommand or the plain render-array shortcut and stop there. Knowing the rest of the family exists — and that you can mix several commands in one response — matters far more than memorizing every one's exact constructor signature.Quick check: you swap out a wrapper's contents with
ReplaceCommand, and your replacement markup forgot to include the original wrapper'sidattribute. What breaks? (Answer: any future AJAX command still targeting thatidselector will silently find nothing and do nothing — the element it used to match no longer exists on the page.)
Key takeaways
- AJAX commands are a fixed, agreed-upon vocabulary — the server sends a named command object, and Drupal's own JavaScript knows how to carry it out.
HtmlCommandreplaces a selector's inner content and keeps the wrapper element alive;ReplaceCommandreplaces the wrapper element itself.- Losing a wrapper's
idvia a carelessReplaceCommandsilently breaks any future command targeting that same selector. - Commands like
OpenModalDialogCommandcan create entirely new UI, not just update an existing wrapper — something the simple render-array shortcut can't do. - In practice, most real-world callbacks only ever need
HtmlCommand,ReplaceCommand, or the plain render-array return.
Coming up next
You've now seen commands that update, replace, and open new content. Next we focus specifically on updating existing form elements in place via AJAX — the pattern behind dependent selects, live validation feedback, and conditionally-appearing fields.