Skip to content

Best Practices

When to Use

Reference this when implementing HTMX features and wanting to follow security, performance, accessibility, and development standards.

Decision

Area Rule Anti-pattern
Security Validate all inputs server-side Trusting client data
Security Return render arrays, not HTML strings Manual HTML strings = XSS risk
Performance Use onlyMainContent() or _htmx_route Full page responses for HTMX endpoints
Performance Cache render arrays with contexts/tags Uncached HTMX responses
Performance Debounce live search (500ms) Request on every keystroke
Accessibility Use aria-live on dynamic regions No screen reader announcements
Accessibility Use semantic HTML elements <div> with HTMX attributes
Code Use dependency injection Static \Drupal:: service calls
Code Use Url objects Hardcoded path strings
Progressive enhancement Form works without JavaScript JavaScript-only form behavior

Pattern

Security — proper validation:

public function buildForm(array $form, FormStateInterface $form_state, string $type = '') {
  if (!in_array($type, $this->getAllowedTypes())) {
    throw new AccessDeniedHttpException();
  }
  // Use render arrays, not raw HTML
  $form['display'] = ['#markup' => $this->renderer->render($safe_build)];
}

Performance — caching:

$build['#cache'] = [
  'keys' => ['my_module', 'content', $entity_id],
  'contexts' => ['url.query_args:page'],
  'tags' => ['node:' . $entity_id],
  'max-age' => 3600,
];

Accessibility — ARIA live region:

$build['results'] = [
  '#type' => 'container',
  '#attributes' => ['id' => 'search-results', 'aria-live' => 'polite', 'aria-atomic' => 'true'],
];

Progressive enhancement:

// Form works as normal POST without JavaScript
$form['#action'] = Url::fromRoute('my.form')->toString();
$form['#method'] = 'post';
// HTMX enhances the experience
(new Htmx())->post(Url::fromRoute('my.form'))->onlyMainContent()->applyTo($form['submit']);

Common Mistakes

  • Wrong: Trusting client input → Right: Always validate server-side
  • Wrong: Not caching HTMX responses → Right: Performance degrades with traffic
  • Wrong: Using static service calls → Right: Breaks testability; use dependency injection
  • Wrong: Building HTML strings → Right: XSS vulnerabilities; use render arrays
  • Wrong: Not testing without JavaScript → Right: Progressive enhancement fails
  • Wrong: Using <div> with HTMX attributes → Right: Use semantic <button>, <a> elements

See Also