Drupal Asset Libraries — Managing CSS and JavaScript in Themes and Modules
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 librarycore/drupal— Core Drupal JavaScriptcore/once— once() function (replaces jQuery.once)core/drupalSettings— Drupal settings objectcore/drupal.ajax— AJAX frameworkcore/ckeditor— CKEditor librarycore/colorbox— Colorbox lightboxcore/matchmedia— matchMedia polyfillcore/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:
- Go to Configuration > Development > Performance
- Check "Aggregate CSS files" and "Aggregate JavaScript files"
- 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
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.
Not specifying dependencies: Skipping dependencies causes JavaScript errors when libraries load in the wrong order. Always declare
dependenciesfor jQuery, Drupal, or other required libraries.Using wrong CSS category: Placing component styles in the
basecategory can break the cascade. Follow SMACSS conventions for predictable styling.Forgetting aggregation in production: Development sites often have aggregation disabled. Before going live, enable CSS and JS aggregation in Performance settings.
Mixing minified and unminified files: When aggregation is enabled, Drupal re-minifies files. Declare already-minified files with
minified: trueto avoid double-Minification.
Practice Questions
What are the five SMACSS CSS categories used in Drupal, and what is the purpose of each?
How do you attach a library to only the homepage template in Twig?
What is the difference between
libraries-overrideandlibraries-extendin a sub-theme's .info.yml?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
Mini Project
Goal: Create and manage asset libraries for a custom theme.
- Create
mytheme.libraries.ymlwith 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)
- Load the global library in
.info.yml - Attach the homepage library in
page--front.html.twig - Attach the gallery library via preprocess when a node has gallery images
- Enable CSS and JS aggregation in Performance settings
- 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