Skip to content

Drupal Asset Libraries — Managing CSS and JavaScript in Themes and Modules

DodaTech Updated 2026-06-27 8 min read

In this tutorial, you'll learn how Drupal asset libraries work — from defining CSS and JavaScript in libraries.yml to managing dependencies, attaching libraries to templates, and optimizing asset delivery for production performance.

What You'll Learn

  • Defining asset libraries in .libraries.yml files for themes and modules
  • SMACSS categorization of CSS files (base, layout, component, state, theme)
  • Library dependencies and how to manage them
  • Attaching libraries globally, per-template, and per-element
  • Optimizing asset delivery with aggregation and CDN

Why It Matters

Without a proper asset management system, your site loads unnecessary CSS and JavaScript on every page. Drupal's library system gives you granular control over what loads where. A contact page does not need carousel JavaScript. An admin page does not need the front-end theme's hero animation. By attaching libraries only where they are needed, you reduce page weight, improve load times, and boost Core Web Vitals scores. This is essential for both user experience and search engine rankings.

Real-World Use

A news website loads different assets for different sections. The homepage loads a hero carousel library and breaking news ticker. Article pages load syntax highlighting for code blocks and social sharing buttons. The admin dashboard loads only Claro admin theme assets. Each page loads only the CSS and JavaScript it needs. The result is a homepage that loads in under two seconds on mobile while article pages have syntax highlighting only when the article contains code.

Learning Path

flowchart LR
  A[Template Suggestions] --> B[Asset Libraries]
  B --> C[Module Management]
  C --> D[Essential Modules]
  D --> E[Caching]
  E --> F[SEO]
  F --> G[Multilingual]

What Are Asset Libraries?

An asset library is a named collection of CSS and JavaScript files, along with dependencies and configuration. Libraries are defined in .libraries.yml files. Themes have one mytheme.libraries.yml file. Modules have one mymodule.libraries.yml file.

# mytheme.libraries.yml
global:
  version: 1.0
  css:
    theme:
      css/style.css: {}
  js:
    js/main.js: {}

homepage:
  version: 1.0
  css:
    component:
      css/components/hero.css: {}
  js:
    js/hero-animation.js: {}

Libraries.yml Structure

Each library has a machine name, version, CSS section, JS section, and optional dependencies.

# Complete library definition structure:
library_name:
  version: 1.0
  css:
    base:
      css/base/reset.css: {}
      css/base/elements.css: {}
    layout:
      css/layout/grid.css: {}
      css/layout/containers.css: { media: all }
    component:
      css/components/buttons.css: {}
      css/components/cards.css: {}
      css/components/navigation.css: {}
    state:
      css/state/active.css: {}
      css/state/disabled.css: {}
    theme:
      css/theme/colors.css: {}
      css/theme/typography.css: {}
  js:
    js/main.js: {}
    js/dropdown.js:
      attributes:
        defer: true
    js/analytics.js:
      attributes:
        async: true
      scope: footer
  dependencies:
    - core/jquery
    - core/drupal
    - core/once

SMACSS Categories

Drupal organizes CSS using SMACSS (Scalable and Modular Architecture for CSS). Each category has a specific purpose:

Base

Base styles are defaults for HTML elements. They include CSS reset, normalize, and basic element styling. These load first and rarely change.

css:
  base:
    css/base/reset.css: {}
    css/base/typography.css: {}
    css/base/forms.css: {}

Layout

Layout styles handle page structure — grids, containers, sidebars, and responsive breakpoints.

css:
  layout:
    css/layout/page.css: {}
    css/layout/grid.css: { media: all }
    css/layout/responsive.css: { media: screen and (max-width: 768px) }

Component

Component styles are reusable UI elements — buttons, cards, headers, modals, forms.

css:
  component:
    css/components/buttons.css: {}
    css/components/cards.css: {}
    css/components/header.css: {}
    css/components/footer.css: {}

State

State styles handle dynamic states like active, disabled, hidden, or loading.

css:
  state:
    css/state/active.css: {}
    css/state/hidden.css: {}
    css/state/disabled.css: {}

Theme

Theme styles are visual customizations — colors, fonts, backgrounds that change per theme.

css:
  theme:
    css/theme/colors.css: {}
    css/theme/fonts.css: {}

JavaScript Configuration

JavaScript files can be configured with attributes and placement options.

js:
  # Load in the <head> (default)
  js/critical.js: {}

  # Load before closing </body>
  js/non-critical.js:
    scope: footer

  # Add HTML attributes
  js/analytics.js:
    attributes:
      async: true
  js/deferred.js:
    attributes:
      defer: true

  # Already minified
  js/vendor.min.js:
    minified: true

  # External URL
  https://cdn.example.com/widget.js:
    type: external
    minified: true

Library Dependencies

Libraries can declare dependencies on other libraries. Drupal loads dependencies before the requiring library.

# Library with dependencies:
slider:
  version: 1.0
  js:
    js/slider.js: {}
  css:
    component:
      css/components/slider.css: {}
  dependencies:
    - core/jquery
    - core/drupal
    - core/once
    - mytheme/carousel

Core Libraries

Drupal core provides many libraries you can depend on:

  • core/jquery — jQuery library
  • core/drupal — Core Drupal JavaScript
  • core/once — once() function (replaces jQuery.once)
  • core/drupalSettings — Drupal settings object
  • core/drupal.ajax — AJAX framework
  • core/ckeditor — CKEditor library
  • core/colorbox — Colorbox lightbox
  • core/matchmedia — matchMedia polyfill
  • core/picturefill — Responsive images polyfill

Attaching Libraries

There are four ways to attach libraries in Drupal.

Globally via .info.yml

# In mytheme.info.yml — loads on every page:
libraries:
  - mytheme/global
  - mytheme/fonts

In Twig Templates

{# Attach library to a specific template #}
{{ attach_library('mytheme/homepage') }}

{# Conditional attachment #}
{% if node.field_has_gallery.value %}
  {{ attach_library('mytheme/gallery') }}
{% endif %}

Via Preprocess Functions

<?php
// In mytheme.theme
function mytheme_preprocess_node(&$variables) {
  $node = $variables['node'];
  
  // Attach a library to all article nodes.
  if ($node->bundle() === 'article') {
    $variables['#attached']['library'][] = 'mytheme/article';
  }
  
  // Attach a library based on a field value.
  if ($node->hasField('field_gallery') && !$node->field_gallery->isEmpty()) {
    $variables['#attached']['library'][] = 'mytheme/gallery';
  }
}

Via PHP in a Module

<?php
// In a custom module
function mymodule_page_attachments(array &$attachments) {
  // Attach a library to every page.
  $attachments['#attached']['library'][] = 'mymodule/global';
  
  // Attach conditionally.
  $current_route = \Drupal::routeMatch()->getRouteName();
  if ($current_route === 'mymodule.custom_page') {
    $attachments['#attached']['library'][] = 'mymodule/custom';
  }
}

// Attach to a render array.
function mymodule_build_block() {
  $build = [
    '#markup' => '<div class="my-block">Content</div>',
    '#attached' => [
      'library' => [
        'mymodule/my-block',
      ],
    ],
  ];
  return $build;
}

Library Overrides in Sub-themes

Sub-themes can override parent theme libraries using libraries-override:

# In sub-theme .info.yml
libraries-override:
  # Replace a parent CSS file
  olivero/global:
    css:
      theme:
        css/style.css: css/custom-style.css
  # Remove a parent library entirely
  olivero/fonts: false
  # Replace the entire library
  olivero/navigation: my_subtheme/custom-navigation

Libraries-Extend

Add your libraries to existing parent libraries without overriding them:

# In sub-theme .info.yml
libraries-extend:
  # Add custom CSS to the parent's dropbutton behavior
  core/drupal.dropbutton:
    - my_subtheme/dropbutton
  # Add analytics to every page
  olivero/global:
    - my_subtheme/analytics

Asset Aggregation

Drupal can aggregate CSS and JavaScript files into fewer HTTP requests. Enable this on production sites.

# In sites/default/services.yml
parameters:
  # Aggregate CSS files
  css.preprocess: true
  # Aggregate JavaScript files
  js.preprocess: true

You can also enable aggregation via the admin UI:

  1. Go to Configuration > Development > Performance
  2. Check "Aggregate CSS files" and "Aggregate JavaScript files"
  3. Save configuration
# Enable via Drush:
drush config-set system.performance css.preprocess 1
drush config-set system.performance js.preprocess 1

Asset Optimization Best Practices

Combine small CSS files into fewer libraries to reduce HTTP requests. Load non-critical JavaScript in the footer using scope: footer. Use defer or async attributes for non-blocking JavaScript. Remove unused libraries from .info.yml if they are not needed globally. Use libraries-extend instead of copying parent library definitions. Enable CSS and JS aggregation on production. Use a CDN (Cloudflare, Fastly) to serve cached assets. Set far-future cache headers for aggregated assets.

Common Mistakes

  1. Loading all libraries globally: Every library in .info.yml loads on every page, including admin pages. Only globally necessary assets should be loaded this way.

  2. Not specifying dependencies: Skipping dependencies causes JavaScript errors when libraries load in the wrong order. Always declare dependencies for jQuery, Drupal, or other required libraries.

  3. Using wrong CSS category: Placing component styles in the base category can break the cascade. Follow SMACSS conventions for predictable styling.

  4. Forgetting aggregation in production: Development sites often have aggregation disabled. Before going live, enable CSS and JS aggregation in Performance settings.

  5. Mixing minified and unminified files: When aggregation is enabled, Drupal re-minifies files. Declare already-minified files with minified: true to avoid double-Minification.

Practice Questions

  1. What are the five SMACSS CSS categories used in Drupal, and what is the purpose of each?

  2. How do you attach a library to only the homepage template in Twig?

  3. What is the difference between libraries-override and libraries-extend in a sub-theme's .info.yml?

  4. Challenge: Plan an asset loading Strategy for a magazine website. Define which libraries would be loaded globally (3-4), which would be attached per-content-type (2-3), and which would be page-specific (homepage, article, gallery). Explain how you would use dependencies to ensure jQuery loads before your custom scripts.

FAQ

What is a Drupal asset library?

An asset library is a named collection of CSS and JavaScript files defined in a .libraries.yml file. Libraries can include dependencies, SMACSS categorization, and configuration for loading behavior.

How do I attach a library to a specific template?

Use the Twig function {{ attach_library('theme/library_name') }} inside the template file. The library loads only when that template is rendered.

What is SMACSS and why does Drupal use it?

SMACSS (Scalable and Modular Architecture for CSS) is a CSS organization methodology. Drupal uses its categories (base, layout, component, state, theme) to ensure consistent, predictable CSS loading order and maintainability.

How do I optimize asset loading in production?

Enable CSS and JS aggregation in Performance settings, use scope: footer for non-critical JavaScript, add defer or async attributes, and use a CDN for asset delivery.

Can I override a parent theme's library in a sub-theme?

Yes. Use libraries-override in the sub-theme's .info.yml to replace individual files, entire libraries, or remove libraries. Use libraries-extend to add your libraries to existing parent libraries.

Mini Project

Goal: Create and manage asset libraries for a custom theme.

  1. Create mytheme.libraries.yml with three libraries:
    • global: base CSS, layout CSS, main.js (with jQuery dependency)
    • homepage: hero component CSS, hero-animation.js (defer, footer)
    • gallery: gallery.css, lightbox.js (depends on core/colorbox)
  2. Load the global library in .info.yml
  3. Attach the homepage library in page--front.html.twig
  4. Attach the gallery library via preprocess when a node has gallery images
  5. Enable CSS and JS aggregation in Performance settings
  6. Verify in the browser that only necessary assets load on each page

What's Next

Now that you understand asset libraries, proceed to module management to learn how to install, update, and uninstall modules. After that, explore essential contributed modules every Drupal site needs.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro