Drupal Themes – Introduction (Foundations)

This article explains what Drupal themes are, how they work, and how they fit into the Drupal rendering system.

It focuses on real-world usage, not just files — so you understand when and why to use themes while building or debugging Drupal sites.


1. What Is a Drupal Theme? (Beginner Friendly)

A Drupal theme controls how the site looks, not how it behaves.

Themes are responsible for:

  • HTML markup
  • Layout and structure
  • CSS styling
  • JavaScript behavior
  • Twig templates

In simple terms:

Modules decide what data exists.
Themes decide how that data is displayed.


2. What a Theme Should and Should NOT Do

Themes SHOULD:

  • Control layout and markup
  • Style content with CSS
  • Render data using Twig
  • Attach frontend libraries (CSS/JS)

Themes SHOULD NOT:

  • Query the database
  • Contain business logic
  • Call external APIs
  • Decide access permissions

If logic lives in a theme, it belongs in a module.


3. Types of Drupal Themes

Drupal supports three main theme types:

Core Themes

Provided by Drupal core

Examples:

  • Olivero
  • Claro

Location:

core/themes/

Contributed Themes

Downloaded from drupal.org

Examples:

  • Bootstrap
  • Barrio
  • Radix

Location:

themes/contrib/

Custom Themes

Built for a specific project

Location:

themes/custom/

Most production sites use custom themes.


4. Minimal Theme Structure

Every Drupal theme must have:

example_theme.info.yml

example_theme.info.yml

name: Example Theme
type: theme
base theme: olivero
core_version_requirement: ^10
libraries:
  - example_theme/global

This tells Drupal:

  • It is a theme
  • Which base theme it extends
  • Which libraries to load

5. Twig  | How Drupal Outputs HTML

Drupal uses Twig for templating.

Example:

<h1>{{ title }}</h1>
{{ content }}

Twig:

  • Receives data from modules
  • Escapes output by default
  • Keeps logic out of templates

Twig templates live in:

/templates

6. Theme Hook Suggestions (Why They Matter)

Drupal chooses templates dynamically using theme hook suggestions.

Example for a node:

node--article.html.twig
node--page.html.twig
node.html.twig

Drupal picks the most specific template available.

This allows precise control without duplicating logic.


7. Libraries | CSS and JavaScript the Drupal Way

Themes attach CSS and JS using libraries.

example_theme.libraries.yml

global:
  css:
    theme:
      css/style.css: {}
  js:
    js/main.js: {}

Libraries are attached:

  • Globally
  • Per template
  • Per component

No hardcoded <link> or <script> tags.


8. Real-World Scenario – Event Listing Page

Scenario:

  • A module provides event data
  • A theme controls how events look

Flow:

Module → Controller → Render Array → Theme → Preprocess Functions → Twig → HTML

The theme:

  • Chooses layout
  • Styles event cards
  • Handles responsive design

The module:

  • Loads data
  • Handles permissions
  • Returns structured data

Clear separation of responsibilities.


9. Where Does Twig Data Come From? (Very Important)

This is a common and very important question.

Twig does not magically know data.

Everything available in Twig comes from Drupal’s render pipeline.

Let’s break it down step by step.


Step 1: Controller or Plugin Creates a Render Array

Most data starts in:

  • Controllers
  • Blocks
  • Views
  • Plugins

Example controller:

public function page() {
  return [
    '#theme' => 'event_list',
    '#title' => 'Upcoming Events',
    '#events' => $events,
  ];
}

This render array defines:

  • Which theme hook to use (#theme)
  • What data should be passed

At this point, there is no Twig yet.


Step 2: Theme Hook Defines Available Variables

The theme hook tells Drupal what variables exist.

Defined via:

  • hook_theme() in a module
  • Or auto-generated (nodes, views, blocks)

Example:

function event_hosting_theme() {
  return [
    'event_list' => [
      'variables' => [
        'title' => NULL,
        'events' => [],
      ],
    ],
  ];
}

This is where Drupal officially says:

These variables are allowed in Twig.


Step 3: Preprocess Functions Modify Variables

Before Twig runs, Drupal calls preprocess functions.

Example:

function example_theme_preprocess_event_list(&$variables) {
  $variables['event_count'] = count($variables['events']);
}

Preprocess functions can:

  • Add new variables
  • Clean or format data
  • Combine values

They run after the render array, but before Twig.


Step 4: Twig Receives Final Variables

Finally, Twig gets the prepared variables:

<h1>{{ title }}</h1>
<p>Total events: {{ event_count }}</p>

<ul>
  {% for event in events %}
    <li>{{ event.name }}</li>
  {% endfor %}
</ul>

Twig:

  • Cannot query data
  • Cannot call services
  • Only renders what it receives

What About JSON Responses?

If a controller returns JSON:

return new JsonResponse($data);

Then:

  • Twig is skipped entirely
  • No preprocess runs
  • Drupal sends raw JSON

Render arrays and Twig are only used for HTML responses.


Simple Mental Model

Controller / Plugin
  → Render Array
    → Theme Hook
      → Preprocess Functions
        → Twig Template
          → HTML

If data is missing in Twig, the issue is always upstream.


10. Themes vs Modules (Quick Comparison)

ThemesModules
Control presentationControl behavior
Twig, CSS, JSPHP, services, hooks
Layout and stylingBusiness logic
No data loadingData and APIs

Why Themes Matter

Understanding themes helps you:

  • Debug markup issues
  • Customize layouts safely
  • Avoid logic in Twig
  • Work cleanly with frontend teams
  • Build upgrade-safe UIs

If you understand themes, you control how Drupal looks without breaking how it works.


Quick Summary

  • Themes control presentation
  • Modules control logic
  • Twig renders HTML
  • Libraries manage CSS and JS
  • Preprocess bridges data and templates
  • Clean separation = maintainable Drupal