Skip to content

JavaScript in SDC Components

When to Use

Adding interactive behavior to Single Directory Components.

Decision

Place JavaScript file in component directory following naming convention. Drupal automatically creates library and attaches JS when component renders. Use standard Drupal.behaviors pattern for initialization.

Pattern

File structure:

components/button/
├── button.component.yml
├── button.twig
├── button.css
└── button.js           # Automatically discovered

Component JavaScript (button.js):

(function (Drupal, once) {
  'use strict';

  Drupal.behaviors.buttonComponent = {
    attach(context) {
      // Target component specifically
      once('button-component', '[data-component="button"]', context).forEach(function (element) {
        // Component initialization
        element.addEventListener('click', function(e) {
          element.classList.toggle('is-active');
        });
      });
    },

    detach(context, settings, trigger) {
      if (trigger === 'unload') {
        // Component cleanup
      }
    }
  };
})(Drupal, once);

Accessing component via data attribute (button.twig):

<button{{ attributes.addClass('button').setAttribute('data-component', 'button') }}>
  {{ label }}
</button>

Library auto-generated: Drupal creates library as core/components.THEME_OR_MODULE--COMPONENT and attaches automatically (Component::getLibraryName()).

Reference: - https://www.drupal.org/docs/develop/theming-drupal/using-single-directory-components - Official SDC docs - https://drupalize.me/tutorial/anatomy-drupal-single-directory-component-sdc - Component anatomy

Common Mistakes

  • Manually creating library for SDC JS - WHY: Drupal does this automatically, creates duplication
  • Not using data attributes for targeting - WHY: Class-based selectors conflict with styling, break encapsulation
  • Global selectors in component JS - WHY: Breaks component reusability, affects other instances
  • Forgetting context parameter - WHY: Breaks when multiple component instances on page

See Also