Skip to content

Drupal Sub-themes — How to Create and Customize a Sub-theme

DodaTech Updated 2026-06-27 8 min read

In this tutorial, you'll learn how to create a Drupal sub-theme — inheriting from a base theme like Olivero or Bootstrap 5, overriding templates, adding custom CSS and JavaScript, and following best practices for maintainable sub-themes.

What You'll Learn

  • What sub-themes are and why they are preferred over modifying base themes directly
  • Choosing the right base theme for your project
  • Creating a sub-theme directory structure and .info.yml file
  • Overriding parent templates and libraries
  • Adding custom CSS, JavaScript, and regions

Why It Matters

Modifying a contributed theme directly is risky. When the theme updates, your changes are overwritten. Sub-themes solve this by inheriting everything from a parent theme while keeping your customizations in a separate, update-safe location. Sub-themes let you leverage existing theme code — often hundreds of hours of development — and customize only what you need. This is standard practice in professional Drupal development and the only safe way to customize contributed themes.

Real-World Use

A digital agency builds Drupal sites for multiple clients, all using the Bootstrap 5 base theme. Each client needs different colors, fonts, and layouts. The agency creates a sub-theme for each client that inherits from Bootstrap 5. The sub-theme overrides only the CSS variables file and a few specific templates. When Bootstrap 5 releases a security update, the agency updates it via Composer, and all client sub-themes inherit the fixes automatically.

Learning Path

flowchart LR
  A[Twig Templating] --> B[Sub-themes]
  B --> C[Template Suggestions]
  C --> D[Asset Libraries]
  D --> E[Module Management]
  E --> F[Essential Modules]
  F --> G[Caching]

What Is a Sub-theme?

A sub-theme is a Drupal theme that inherits resources from a parent theme. The sub-theme contains only the files that differ from the parent. When Drupal renders a page, it first looks for templates and assets in the sub-theme, then falls back to the parent theme.

Sub-theme (my_custom_theme)
├── my_custom_theme.info.yml  (declares base theme)
├── css/style.css             (overrides parent CSS)
└── templates/
    └── node.html.twig        (overrides parent template)

Parent theme (olivero)
├── olivero.info.yml
├── css/
│   ├── components/
│   └── layout/
├── js/
└── templates/
    ├── node.html.twig        (fallback if sub-theme lacks it)
    ├── page.html.twig
    └── block.html.twig

Choosing a Base Theme

The base theme determines your starting point. Consider these options:

Olivero — Drupal 10's default front-end theme. Accessible, modern, good starting point for content-focused sites. Limited layout options without customization.

Bootstrap 5 — responsive framework with built-in grid, components, and utility classes. Best for sites that need rapid prototyping and Responsive Design.

Stable 9 — minimal base theme with almost no CSS. Best for experienced developers who want complete control and minimal bloat.

Classy — provides more default styles than Stable. Good for beginners who need visible progress without writing much CSS.

# Example .info.yml with different base themes:

# Olivero sub-theme:
base theme: olivero

# Bootstrap 5 sub-theme:
base theme: bootstrap5

# Stable 9 sub-theme:
base theme: stable9

Sub-theme Directory Structure

A minimal sub-theme needs only an .info.yml file. Additional files are added as needed.

themes/custom/my_custom_theme/
├── my_custom_theme.info.yml
├── my_custom_theme.libraries.yml
├── my_custom_theme.breakpoints.yml
├── my_custom_theme.schema.yml
├── screenshot.png
├── css/
│   └── style.css
├── js/
│   └── main.js
└── templates/
    ├── node.html.twig
    ├── page.html.twig
    └── block.html.twig

Sub-theme .info.yml

The .info.yml file is the only required file. It must include the base theme key pointing to the parent theme.

# my_custom_theme.info.yml
name: 'My Custom Theme'
type: theme
core_version_requirement: ^10 || ^11
description: 'A custom sub-theme based on Olivero.'
base theme: olivero
package: Custom
version: 1.0.0

libraries:
  - my_custom_theme/global

libraries-override:
  olivero/global:
    css:
      theme:
        css/style.css: css/custom-style.css

libraries-extend:
  core/drupal.dropbutton:
    - my_custom_theme/dropbutton

regions:
  header: 'Header'
  primary_menu: 'Primary Menu'
  content: 'Content'
  sidebar: 'Sidebar'
  footer: 'Footer'

regions_hidden: []

Inheriting Templates

Sub-themes automatically inherit all templates from the parent theme. You only need to create a template file in your sub-theme if you want to override it.

To override node.html.twig:

  1. Find the original template in the parent theme at themes/contrib/olivero/templates/node.html.twig
  2. Copy it to themes/custom/my_custom_theme/templates/node.html.twig
  3. Modify the copied template

The template path in the sub-theme must match the relative path used in the parent theme.

# Locate a template in the parent theme:
find themes/contrib/olivero -name "node.html.twig"

# Copy to sub-theme:
cp themes/contrib/olivero/templates/node.html.twig \
   themes/custom/my_custom_theme/templates/node.html.twig

Overriding Libraries

Use libraries-override in .info.yml to replace or modify parent theme libraries.

# Replace a parent CSS file with your own:
libraries-override:
  olivero/global:
    css:
      theme:
        css/style.css: css/custom-style.css

# Remove a parent library entirely:
libraries-override:
  olivero/fonts: false

# Override a JavaScript file:
libraries-override:
  olivero/navigation:
    js:
      js/navigation.js: js/custom-navigation.js

Adding Custom CSS and JS

Define new libraries in your sub-theme's .libraries.yml file.

# my_custom_theme.libraries.yml
global:
  version: 1.0
  css:
    component:
      css/components/cards.css: {}
    theme:
      css/custom-style.css: {}
  js:
    js/main.js: {}
    js/custom.js:
      attributes:
        defer: true

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

Load these libraries in .info.yml globally or in Twig templates:

# In .info.yml to load globally:
libraries:
  - my_custom_theme/global
{# In a specific template: #}
{{ attach_library('my_custom_theme/homepage') }}

Libraries-Extend

The libraries-extend key adds your libraries to existing parent libraries. Use this to add CSS or JS to specific parent theme behaviors.

libraries-extend:
  # Add our custom styles to the parent's dropbutton library
  core/drupal.dropbutton:
    - my_custom_theme/dropbutton
  # Extend Olivero's global library with our custom print styles
  olivero/global:
    - my_custom_theme/print-styles

Sub-theme Regions Override

A sub-theme can define different regions than its parent. The regions from the sub-theme completely replace the parent's regions.

# Sub-theme with custom regions:
regions:
  header_top: 'Header Top'
  header_bottom: 'Header Bottom'
  highlighted: 'Highlighted'
  content: 'Content'
  sidebar_left: 'Left Sidebar'
  sidebar_right: 'Right Sidebar'
  footer_first: 'Footer First'
  footer_second: 'Footer Second'
  footer_third: 'Footer Third'

Sub-theme Settings

Create a .schema.yml file to provide custom theme settings with a UI.

# my_custom_theme.schema.yml
my_custom_theme.settings:
  type: theme_settings
  label: 'My Custom Theme settings'
  mapping:
    layout_width:
      type: string
      label: 'Layout Width'
    header_style:
      type: string
      label: 'Header Style'
    footer_columns:
      type: integer
      label: 'Footer Columns'

Sub-theme Screenshot

A screenshot.png in the sub-theme root appears on the Appearance page. Size it at 293 by 68 pixels.

# Create a simple screenshot:
convert -size 293x68 "xc:#0044cc" -fill white \
  -pointsize 20 -gravity center \
  -annotate 0 "My Custom Theme" \
  screenshot.png

Best Practices

Do not override every template. Override only what you need. Each override creates maintenance work when the parent theme updates. Document every override by adding comments at the top of each overridden file noting the date and reason for the override. Test after every parent theme update to ensure overrides still work. Use libraries-override sparingly — prefer libraries-extend when you only need to add assets. Keep the sub-theme minimal. A sub-theme with only an .info.yml file is valid if you only need to change theme settings.

Common Mistakes

  1. Modifying the parent theme directly: Changes to contributed themes are lost on update. Always create a sub-theme for customizations.

  2. Missing the base theme key: Without base theme in .info.yml, the sub-theme is treated as a standalone theme and inherits nothing.

  3. Wrong template path: Templates in the sub-theme must match the same relative path as in the parent. A template in the wrong path is ignored.

  4. Not clearing cache after adding templates: Twig caches template locations. Run drush cr after adding new template files to the sub-theme.

  5. Overriding too much: Copying every parent template defeats the purpose of a sub-theme. Override only the templates you need to customize.

Practice Questions

  1. What is the advantage of creating a sub-theme instead of modifying a contributed theme directly?

  2. How does the libraries-override key in .info.yml differ from libraries-extend?

  3. When you create a template file in a sub-theme, how does Drupal decide whether to use the sub-theme template or fall back to the parent?

  4. Challenge: Plan a sub-theme for a corporate website using Bootstrap 5 as the base theme. List the templates you would override (at least 3), the libraries you would add, and the regions you would define. Explain why each override is necessary and how you would handle Bootstrap 5 updates.

FAQ

What is a sub-theme in Drupal?

A sub-theme is a theme that inherits all templates, assets, and configuration from a parent theme. It contains only the files that differ from the parent, making it update-safe and maintainable.

How do I create a sub-theme?

Create a directory in themes/custom/ with a .info.yml file that includes the base theme key pointing to the parent theme. Add only the templates and assets you want to override.

Can a sub-theme have its own regions?

Yes. Regions defined in the sub-theme's .info.yml completely replace the parent's regions. This lets you define a completely different layout while inheriting templates and assets.

How do I override a CSS file from the parent theme?

Use libraries-override in .info.yml. For example: libraries-override: olivero/global: css/theme: css/style.css: css/custom-style.css replaces the parent's style.css with your own.

Is the screenshot.png required?

No. The theme works without it, but the Appearance page shows a placeholder icon. A screenshot makes your theme look professional. The recommended size is 293 by 68 pixels.

Mini Project

Goal: Create a complete sub-theme based on Olivero.

  1. Create themes/custom/my_olivero/ directory
  2. Create my_olivero.info.yml with base theme as olivero, name, description, and one region override (add a "promo" region)
  3. Create my_olivero.libraries.yml with a global library containing custom-style.css
  4. Create css/custom-style.css that changes the primary color to a custom hex value
  5. Override templates/page.html.twig to add the promo region above the content
  6. Create screenshot.png for the theme
  7. Enable the sub-theme on the Appearance page
  8. Verify it inherits Olivero's templates but uses your custom color

What's Next

Now that you can create sub-themes, proceed to template suggestions for fine-grained control over which template renders each piece of content. After that, explore asset libraries to manage CSS and JavaScript efficiently.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro