Europe/London
Feb 2026
Career
2 min read

Drupal 10 JavaScript upgrade: from jQuery .once() to the standalone API

A practical guide to the breaking change most Drupal 9 → 10 migrations get wrong on the first pass.

Drupal 10 dropped jQuery's .once() plugin and replaced it with a standalone once() library. This is the single most common breakage point in Drupal 9 → 10 module upgrades, and it fails silently in a way that's easy to miss in testing.

What changed and why

In Drupal 9, jQuery was a hard dependency and .once() was a jQuery plugin. Drupal 10 began decoupling from jQuery. The once() functionality was extracted into its own standalone library — same behaviour, completely different API.

// Drupal 9 — jQuery .once()
Drupal.behaviors.myBehavior = {
  attach: function (context) {
    $(context).find('.my-element').once('myBehavior').each(function () {
      // runs once per element
    });
  }
};
// Drupal 10 — standalone once()
Drupal.behaviors.myBehavior = {
  attach: function (context) {
    once('myBehavior', '.my-element', context).forEach(function (el) {
      // runs once per element
    });
  }
};

The library declaration

You also need to update your .libraries.yml — otherwise once won't be available at runtime even if the syntax is correct.

# mymodule.libraries.yml
mymodule/behaviors:
  js:
    js/my-behavior.js: {}
  dependencies:
    - core/once       # <-- required in Drupal 10
    - core/drupal

Migration tip: Run grep -r "\.once(" web/modules/custom to find every occurrence across your custom modules at once. There are usually more than you expect. Check contrib modules too — not all have been updated.

The removal of jQuery as a hard dependency

Drupal 10 still ships jQuery but it's no longer automatically loaded on every page. If your custom modules use jQuery directly, add core/jquery to your libraries dependencies explicitly. Don't assume it's there.