Skip to content

Best Practices: Performance

When to Use

AJAX requests are slow, database queries are inefficient, or large operations cause timeouts.

Performance Optimization Strategies

Performance Optimization Strategies:

  1. Minimize DOM Updates
  2. Return smallest possible element, not entire form
  3. Use HtmlCommand instead of ReplaceCommand when wrapper unchanged
  4. Batch multiple updates into single AJAX response

  5. Database Optimization

  6. Always use range(0, N) to limit query results
  7. Load only needed fields with loadMultiple() instead of full entities
  8. Use accessCheck(TRUE) to leverage query access caching
  9. Index custom fields used in AJAX queries

  10. Batch Processing

  11. Use Batch API for operations processing >100 items
  12. Set progress indicators for operations >2 seconds
  13. Break large operations into chunks to prevent timeouts

  14. Caching

  15. Use CacheableAjaxResponse for cacheable content
  16. Configure proper cache contexts (user.permissions, languages, etc.)
  17. Add cache tags for automatic invalidation
  18. Set realistic max-age (match content update frequency)

  19. Asset Optimization

  20. Aggregate JavaScript/CSS in production
  21. Use #attached libraries instead of AddJsCommand/AddCssCommand
  22. Lazy load libraries only when needed
  23. Minimize third-party dependencies

Performance Thresholds

Performance Thresholds:

Operation Type Target Time Action if Exceeded
Simple form field update <200ms Optimize query, reduce DOM update size
Autocomplete query <500ms Add result limit, index search fields
File upload <5s for 2MB Use progress bar, increase PHP limits
Batch operation <30s total Use Batch API with progress tracking

Pattern

// 1. Return smallest element
public function ajaxCallback(array &$form, FormStateInterface $form_state) {
  return $form['subcategory'];  // NOT return $form
}

// 2. Always limit query results
$nids = $this->entityTypeManager->getStorage('node')->getQuery()
  ->condition('type', 'article')
  ->range(0, 50)
  ->accessCheck(TRUE)
  ->execute();

// 3. Use Batch API for large operations (>100 items / >30 seconds)
public function processBatch(array &$form, FormStateInterface $form_state) {
  $batch = [
    'operations' => [[[$this, 'processBatchOp'], [range(0, 99)]]],
    'finished' => [$this, 'batchFinished'],
  ];
  batch_set($batch);
  return batch_process();
}

// 4. Use #attached instead of AddCssCommand/AddJsCommand
$build['#attached']['library'][] = 'my_module/dynamic-feature';

// 5. Progress indicator for slow operations
$form['trigger']['#ajax']['progress'] = [
  'type' => 'bar',
  'url' => Url::fromRoute('my_module.batch_progress')->toString(),
  'interval' => 1000,
];

See Also