JavaScript Coding Standards

Drupal pages don't always load from scratch — an AJAX request can replace just part of the page and leave the rest untouched — so Drupal's JavaScript standards exist largely to make sure behavior attaches correctly no matter how a piece of markup ended up on the page.

1. Drupal.behaviors

Instead of code that runs once on page load, Drupal JavaScript is organized into behaviors: objects with an attach() method that Drupal calls on the initial page load and again after every AJAX update, passed the specific part of the DOM (context) that just changed.

Drupal.behaviors.productImportStatus = {
attach(context, settings) {
// Runs on page load, and again for every AJAX-inserted fragment.
},
};

2. once()

Because attach() can run more than once against overlapping markup, code that shouldn't run twice on the same element wraps its element selection in once(), which returns only the elements that haven't been processed by that particular call site before.

// ❌ Incorrect: runs again on every AJAX partial rebuild.
Drupal.behaviors.example = {
attach(context) {
context.querySelectorAll('.my-element').forEach((element) => {
element.addEventListener('click', handleClick);
});
},
};

// ✅ Correct: once() keeps this from running twice on the same element.
Drupal.behaviors.example = {
attach(context) {
once('example', '.my-element', context).forEach((element) => {
element.addEventListener('click', handleClick);
});
},
};

The first argument to once() is an ID unique to that call site — it's what lets two different behaviors both process the same element independently, without stepping on each other.

3. drupalSettings

Data that PHP needs to hand off to JavaScript — an API endpoint, a module's configuration, a piece of server-computed state — is attached to drupalSettings rather than written into a global variable or embedded directly in markup.

$build['#attached']['drupalSettings']['productImport']['apiEndpoint'] = $endpoint;
Drupal.behaviors.productImport = {
attach(context, settings) {
fetch(settings.productImport.apiEndpoint);
},
};

4. Modern Syntax

Drupal's JavaScript standards are built on ES6+: let and const in place of var, arrow functions where this doesn't need to be rebound, and template literals over string concatenation. const is the default; let is reached for only when a variable genuinely needs to be reassigned.

// ❌ Incorrect: var, and string concatenation.
var message = 'Imported ' + count + ' products.';

// ✅ Correct: const, and a template literal.
const message = `Imported ${count} products.`;

5. Formatting

Indentation is 2 spaces, statements end in semicolons, and strings use single quotes unless the string itself contains one. Files end with a single trailing newline, same as PHP.

// ❌ Incorrect.
function greet(name){
    return "Hello, "+name
}

// ✅ Correct.
function greet(name) {
  return `Hello, ${name}`;
}

6. ESLint

Drupal core ships an ESLint configuration covering all of the above, and running it locally — or wiring it into an editor — catches most of this automatically, the same way PHP_CodeSniffer does for PHP. It's not a substitute for understanding the rules, but it does mean none of them need to be checked by hand on every commit.

Latest update: 21.09.2026 09:20