Writing AJAX Callbacks That Return AjaxResponsefor Drupal 8 , 9 , 10 , and 11

Last updated :  

Last lesson's updateColor() callback returned a plain render array — Drupal quietly wrapped that in an AJAX response and swapped it into the wrapper for you. That shortcut works for the single most common case: "replace this wrapper's contents with this markup." But sometimes you need more than one thing to happen — update a message, append new content, and refocus an element, all from one callback. That's what building your own AjaxResponse object by hand unlocks: full control over exactly which commands run, in what order.

What you'll learn in this lesson

  • The difference between returning a render array and returning an explicit AjaxResponse
  • How to construct an AjaxResponse and attach commands to it with addCommand()
  • What an AJAX command actually is, and why there's a whole family of them
  • Why you'd reach for this pattern instead of the simpler render-array shortcut

Two valid return types for an AJAX callback

An AJAX callback function is allowed to return one of two things:

  • A render array — Drupal renders it to HTML and replaces the wrapper element's contents. This is what you saw last lesson, and it's all you need for a single, simple replacement.
  • An explicit AjaxResponse object — you build it yourself and attach one or more commands to it, giving you control over more than just "replace this one wrapper."

Building an AjaxResponse by hand

use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\AppendCommand;

public function myCallback(array &$form, FormStateInterface $form_state) {
  $output = $this->t('You typed: @value', ['@value' => $form_state->getValue('text_input')]);

  $response = new AjaxResponse();
  $response->addCommand(new AppendCommand('#text-output-wrapper', $output));
  return $response;
}

Three steps, every time: instantiate a bare AjaxResponse, call addCommand() once per action you want the browser to take, then return the response object itself instead of a render array. AppendCommand is one command among many — it adds content inside a target selector without disturbing what's already there, which is a genuinely different effect from replacing a wrapper's entire contents.

Why this matters beyond "more options": the render-array shortcut can only ever do one thing — replace one wrapper's HTML. The moment you need to update two different parts of the page from a single AJAX request, or run a command that isn't "replace HTML" at all (scroll to an element, add a CSS class, show a message), the render-array shortcut has no way to express that. An explicit AjaxResponse with multiple addCommand() calls is the only way to do several distinct things in one round trip.

Quick check: could you call addCommand() three times on the same AjaxResponse object, targeting three different wrapper elements? (Answer: yes — that's exactly the situation an explicit AjaxResponse exists for. Each addCommand() call is independent; the browser executes them in the order you added them.)

Key takeaways

  • An AJAX callback may return either a plain render array (simplest case, one wrapper replaced) or a hand-built AjaxResponse object (full control, multiple actions per request).
  • Build an explicit response with new AjaxResponse(), then call addCommand() once per action.
  • Commands are executed by the browser in the order they were added to the response.
  • Reach for an explicit AjaxResponse the moment you need more than one page update, or an action that isn't a simple HTML swap, from a single callback.

Coming up next

You've now met AppendCommand as one example of an AJAX command — the next lesson catalogs the rest of the family Drupal ships with, and when to reach for each one.