Drupal Themes, Layouts & Extensions — Complete Theming Guide
Drupal uses Twig (the templating engine from Symfony) for theme templates. If you’ve used Jinja2 in Python or Liquid in Shopify, Twig will feel familiar. Think of a theme as the costume your content wears — the same article can look completely different depending on which theme wraps around it.
What You’ll Learn
- Drupal’s theme structure —
.info.yml,.libraries.yml, Twig templates - Creating a custom theme and sub-theme
- Using Layout Builder for drag-and-drop page layouts
- Installing and managing contributed modules via Composer
- Creating a custom module with routing and hooks
Why Theming and Extensibility Matter
Drupal’s theming system separates content from presentation. Your editors create structured content (articles with fields) and your theme decides how to display that content (grid, list, card, table). This means you can redesign your entire site by switching themes without touching a single piece of content.
The same separation powers DodaTech’s tutorial platform — structured content in the database, Twig templates deciding the HTML output. Even Durga Antivirus Pro uses template-based rendering for its documentation.
flowchart LR
A["Menus, Blocks & Taxonomies"] --> B["Themes, Layouts & Extensions<br/><strong>You are here</strong>"]:::current
B --> C["Users, Security & Administration"]
classDef current fill:#38bdf8,color:#0f172a,stroke-width:2px;
Theme Structure
A Drupal theme lives in themes/custom/ and consists of:
mytheme/
├── mytheme.info.yml # Theme metadata & configuration
├── mytheme.libraries.yml # Asset library definitions
├── mytheme.theme # Theme hook overrides (PHP)
├── templates/ # Twig template overrides
│ ├── node.html.twig
│ ├── page.html.twig
│ └── block.html.twig
├── dist/ # Compiled assets
│ ├── css/
│ └── js/
└── src/ # Source files (SCSS, JS)
├── scss/
└── js/The .info.yml File
This is the identity card of your theme. WordPress uses style.css headers; Drupal uses a YAML file.
name: 'My Theme'
type: theme
core_version_requirement: ^10
description: 'Custom theme for My Site'
base theme: false
# Define regions where blocks can be placed
regions:
header: Header
primary_menu: 'Primary menu'
secondary_menu: 'Secondary menu'
content: Content
sidebar_first: 'Left sidebar'
sidebar_second: 'Right sidebar'
footer: Footer
# Load libraries globally
libraries:
- mytheme/global-stylesAsset Libraries
Every CSS and JS file must be declared in .libraries.yml:
global-styles:
version: 1.x
css:
theme:
dist/css/style.css: { minified: true }
js:
dist/js/main.js: { minified: true }Default Themes
Drupal 10 ships with three themes:
- Olivero — Modern, accessible front-end theme (default)
- Claro — Administration theme designed for usability
- Stark — Bare-bones theme for debugging (no CSS, no JS)
Twig Templates
Twig is Drupal’s templating engine. Templates follow a strict naming hierarchy:
node--article--teaser.html.twig → Article content type, teaser view mode
node--article.html.twig → Article content type, any view mode
node.html.twig → Any node, any view mode
page.html.twig → Page-level template
block.html.twig → Block template{# Override node.html.twig for the Article content type as a teaser #}
{# File: templates/node/node--article--teaser.html.twig #}
<article{{ attributes.addClass('article-teaser') }}>
{{ title_prefix }}
{% if not page %}
<h2{{ title_attributes }}>
<a href="{{ url }}" rel="bookmark">{{ label }}</a>
</h2>
{% endif %}
{{ title_suffix }}
{% if display_submitted %}
<footer class="author">
{{ author_picture }}
<div{{ author_attributes }}>
{% trans %}Submitted by {{ author_name }} on {{ date }}{% endtrans %}
{{ metadata }}
</div>
</footer>
{% endif %}
{% if content.field_image|render %}
<div class="teaser-image">{{ content.field_image }}</div>
{% endif %}
<div{{ content_attributes }}>
{{ content.body|render|striptags|slice(0, 300) }}...
</div>
</article>Sub-themes
A sub-theme inherits all templates and assets from a parent theme. Use a sub-theme when customizing Olivero or a contributed theme — your changes survive parent updates.
# my_subtheme.info.yml
name: 'My Subtheme'
type: theme
core_version_requirement: ^10
base theme: olivero
libraries:
- my_subtheme/globalCreate a new templates/ folder in your sub-theme and copy only the templates you need to override. Everything else comes from the parent.
Layout Builder
Layout Builder is Drupal’s drag-and-drop page builder — in core, no extra module needed.
Enabling Layout Builder
- Extend → Layout Builder and Layout Discovery (both core modules)
- Structure → Content types → Manage display → Layout
- Click Use Layout Builder → Save
Building Layouts
Once enabled, content editors can:
- Add sections (one, two, three, or four columns)
- Drag blocks into section regions
- Configure per-block visibility and styling
- Override layout on individual content items (different layout per article)
{# Layout Builder section template override #}
{# templates/layouts/layout--twocol-section.html.twig #}
{%
set classes = [
'layout',
'layout--twocol-section',
'layout--twocol-section--' ~ settings.percentage,
]
%}
<div{{ attributes.addClass(classes) }}>
{% if content.first %}
<div {{ region_attributes.first.addClass('layout__region', 'layout__region--first') }}>
{{ content.first }}
</div>
{% endif %}
{% if content.second %}
<div {{ region_attributes.second.addClass('layout__region', 'layout__region--second') }}>
{{ content.second }}
</div>
{% endif %}
</div>Extensions & Modules
Modules add functionality. Core modules ship with Drupal; contributed modules come from drupal.org; custom modules are built by your team.
Installing Contributed Modules
Always use Composer:
# Install Pathauto (automatic URL aliases)
composer require drupal/pathauto
# Install Admin Toolbar (better admin navigation)
composer require drupal/admin_toolbar
# Install a specific version
composer require drupal/token:^1.12Essential Contributed Modules
| Module | Purpose |
|---|---|
| Admin Toolbar | Better admin navigation |
| Pathauto | Automatic URL aliases |
| Token | Token replacement system |
| Redirect | URL redirect management |
| Webform | Form builder |
| Paragraphs | Flexible content components |
| Metatag | SEO meta tags |
| XML Sitemap | SEO sitemap generation |
Creating a Custom Module
# mymodule/mymodule.info.yml
name: 'My Module'
type: module
core_version_requirement: ^10
description: 'Example custom module'<?php
// mymodule/mymodule.module
use Drupal\Core\Routing\RouteMatchInterface;
/**
* Implements hook_help().
*/
function mymodule_help($route_name, RouteMatchInterface $route_match) {
switch ($route_name) {
case 'help.page.mymodule':
return '<p>This is my custom module!</p>';
}
}
/**
* Implements hook_preprocess_node().
*/
function mymodule_preprocess_node(array &$variables) {
if ($variables['node']->bundle() === 'article') {
$variables['attributes']['class'][] = 'article--custom';
}
}# mymodule/mymodule.routing.yml
mymodule.hello:
path: '/hello'
defaults:
_controller: '\Drupal\mymodule\Controller\HelloController::hello'
_title: 'Hello World'
requirements:
_permission: 'access content'Common Mistakes
1. Editing Core or Contributed Themes Directly
Changes to Olivero, Claro, or contributed themes are lost on update. Always create a custom theme or sub-theme.
2. Ignoring the Asset Library System
Drupal requires libraries declared in .libraries.yml. Adding CSS via #attached in a template or deprecated drupal_add_css() won’t work properly.
3. Not Using Cache Tags
When creating custom block or plugin output, attach cache tags so Drupal’s cache invalidation works:
$build['#cache']['tags'][] = 'node:' . $node->id();4. Overriding Too Many Templates
Start with minimal overrides. Use hook_preprocess_HOOK for small changes instead of copying entire template files.
5. Not Clearing Cache After Module Changes
After installing or updating modules, run drush cr — otherwise Drupal’s cache won’t recognize the new module’s routes and hooks.
Practice Questions
What is the difference between a module and a theme?
Answer: A module provides functionality (business logic, API endpoints, data processing). A theme provides visual presentation (templates, CSS, JavaScript). Modules can alter themes, but themes should not contain business logic.What is the Template: line in a child theme’s info.yml?
Answer: In Drupal, it’s calledbase theme. It tells Drupal which theme is the parent. Templates not present in the child are automatically loaded from the parent.Why should you use Composer for contributed modules?
Answer: Composer manages dependencies, versions, and updates. Downloading modules manually makes updates difficult and can lead to version conflicts.Challenge: Create a sub-theme of Olivero with custom CSS that changes the site’s primary color to a shade of blue. Enable Layout Builder for Articles and create a two-column layout with an image in the left column and text in the right column.
FAQ
Try It Yourself
- Create a custom theme with a single CSS file that changes heading colors
- Enable and set it as the default theme via Drush
- Install Admin Toolbar via Composer and enable it
- Enable Layout Builder for the Basic Page content type
- Create a page with a custom two-column layout using Layout Builder
What’s Next
| Topic | Description |
|---|---|
| Users, Security & Administration | User roles, permissions, security hardening, multilingual |
| Drupal Developer Reference | Hooks, entities, render arrays, Drush commands |
| CSS | Styling Drupal themes |
| PHP | Custom module development |
| HTML | Understanding Drupal’s template output |
What’s Next
Congratulations on completing this Drupal Themes Layouts Extensions tutorial! Here’s where to go from here:
- Practice daily — Consistency is more important than long study sessions
- Build a project — Apply what you learned by building something real
- Explore related topics — Check out other tutorials in the same category
- Join the community — Discuss with other learners and share your progress
Remember: every expert was once a beginner. Keep coding!
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro