Skip to content

AJAX Core Concepts

When to Use

Use Drupal AJAX when maintaining existing codebases, working with contributed modules, or requiring ordered command sequences and the core dialog system. See Drupal HTMX Development Guide for HTMX-based approaches available in Drupal 11.3+.

Decision

Layer Purpose Key Components
Server-side Process requests, build responses FormStateInterface, AjaxResponse, Commands
Client-side Handle events, execute commands Drupal.ajax, Behaviors API, jQuery
Integration Connect server to client #ajax property, routes, callbacks

Pattern

public function buildForm(array $form, FormStateInterface $form_state) {
  $form['trigger'] = [
    '#type' => 'select',
    '#title' => t('Category'),
    '#ajax' => [
      'callback' => '::ajaxCallback',
      'wrapper' => 'target-wrapper',
    ],
  ];

  $form['target'] = [
    '#prefix' => '<div id="target-wrapper">',
    '#suffix' => '</div>',
  ];

  return $form;
}

public function ajaxCallback(array &$form, FormStateInterface $form_state) {
  return $form['target'];  // Return render array or AjaxResponse
}

Workflow:

1. User triggers event (click, change, keyup)
   ↓
2. JavaScript sends AJAX request to server
   ↓
3. PHP callback processes FormStateInterface
   ↓
4. Server returns AjaxResponse with commands
   ↓
5. Client executes commands, updates DOM
   ↓
6. Drupal.behaviors.attach() runs on new content

Reference: core/lib/Drupal/Core/Form/FormBuilderInterface.php

Common Mistakes

  • Wrong: Returning HTML string from callback → Right: Always return render arrays or AjaxResponse objects
  • Wrong: Missing wrapper element → Right: Wrapper must exist in DOM before AJAX triggers
  • Wrong: Omitting #prefix/#suffixRight: ReplaceCommand needs wrapper ID in returned content
  • Wrong: Skipping $form_state->setRebuild()Right: Required for multi-step workflows to rebuild properly
  • Wrong: Static calls instead of dependency injection → Right: Inject services for testability and caching

See Also