Introduction

The evolution of frontend development has shifted the paradigm from page-level thinking to component-level thinking. Frameworks like React, Vue, and Angular popularized this model — where the UI is composed of reusable, self-contained building blocks. Each component manages its own markup, styles, behavior, and state.

Drupal, traditionally, has operated under a different model. Theming in Drupal has historically been template-driven, where a template like node--article.html.twig renders an entire content type, often with deeply nested template overrides and preprocess functions scattered across .theme files.

This approach creates several problems at scale:

Component-Based Design (CBD) brings the frontend's modular architecture philosophy to Drupal's theming layer. Instead of thinking in terms of "the Article page template," you think in terms of reusable components: a Card, a Hero Banner, a Teaser, a Media Block — each with a defined API and its own encapsulated assets.

What Is a Component in Drupal?

A component in this context is a reusable UI unit with:

  1. A Twig Template — The HTML structure
  2. Scoped CSS — Styles that only affect this component
  3. Optional JavaScript — Interactive behavior
  4. A Schema/Contract — A clear definition of what data (props) the component accepts

With Drupal 10.1's Single Directory Components (SDC), this structure is now a first-class citizen:

components/
└── teaser/
    ├── teaser.twig
    ├── teaser.css
    ├── teaser.js
    └── teaser.component.yml

The .component.yml file acts as the contract — it defines the expected properties, types, and defaults. This is conceptually identical to a React component's propTypes or TypeScript interface.

From Templates to Components: The Paradigm Shift

Traditional Drupal Theming

templates/
├── node--article.html.twig
├── node--article--teaser.html.twig
├── block--sidebar.html.twig
├── views-view-unformatted--latest-posts.html.twig
└── field--body.html.twig

Problems: Each template is tightly coupled to its Drupal entity. Shared visual patterns are duplicated across templates. Styles are organized by template, not by visual component.

Component-Based Approach

components/
├── card/
│   ├── card.twig
│   ├── card.css
│   └── card.component.yml
├── hero/
│   ├── hero.twig
│   ├── hero.css
│   └── hero.component.yml
└── teaser/
    ├── teaser.twig
    ├── teaser.css
    └── teaser.component.yml

Then, Drupal's templates become thin wrappers that simply map entity data to component props:

{# node--article--teaser.html.twig #}
{% include 'mytheme:teaser' with {
  title: label,
  image: content.field_image,
  summary: content.body|render|striptags|trim,
  url: url,
  date: node.getCreatedTime|date('M d, Y'),
} %}

This is the core architectural shift: Drupal templates stop being about layout and start being about data mapping.

Benefits of Component-Based Architecture

1. Reusability — Build a Card component once. Use it to render articles, events, products, and custom entities. The same component handles all of them — only the data changes.

2. Encapsulation — Each component's CSS is scoped. A .card__title style won't accidentally affect a .hero__title. No more cascading conflicts.

3. Testability — With tools like Storybook, each component can be developed, tested, and documented in isolation — without needing a running Drupal site.

4. Clear Contracts — The .component.yml file explicitly states what a component expects. If a required property is missing, it's immediately visible.

5. Parallel Development — Frontend developers can build and iterate on components while backend developers work on content modeling and APIs. They meet at the data-mapping layer.

Structuring Your Component Library

A well-structured library follows Atomic Design principles:

Data Mapping: The Bridge Between CMS and Components

The most critical piece of component-based Drupal is the data mapping layer. Your components don't know about Drupal entities. They accept clean, typed props. Your Drupal templates (or preprocess functions) handle the transformation.

function mytheme_preprocess_node__article__teaser(&$variables) {
  $node = $variables['node'];
  $variables['component_data'] = [
    'title' => $node->getTitle(),
    'image' => $node->get('field_image')->entity?->getFileUri(),
    'summary' => strip_tags($node->get('body')->value),
    'url' => $node->toUrl()->toString(),
    'date' => \Drupal::service('date.formatter')
      ->format($node->getCreatedTime(), 'custom', 'M d, Y'),
  ];
}

Then in the template:

{% include 'mytheme:card' with component_data %}

CSS Architecture for Components

Each component should own its styles. Use BEM naming to avoid conflicts:

/* card/card.css */
.card { /* Block */ }
.card__image { /* Element */ }
.card__title { /* Element */ }
.card--featured { /* Modifier */ }
.card--compact { /* Modifier */ }

Share design tokens through CSS custom properties:

:root {
  --color-primary: #0057ff;
  --color-text: #1a1a1a;
  --spacing-md: 1.5rem;
  --font-heading: 'Inter', sans-serif;
  --radius-md: 8px;
}

.card {
  border-radius: var(--radius-md);
  font-family: var(--font-heading);
  padding: var(--spacing-md);
}

Migration Strategy

If you're working with an existing Drupal theme, here's a practical migration path:

  1. Audit your templates — Identify repeated visual patterns. Every pattern that appears more than once is a component candidate.
  2. Start with Atoms — Extract buttons, badges, and form elements first. These are small, low-risk, and high-reuse.
  3. Create component contracts — Write .component.yml files for each new component.
  4. Build thin templates — Refactor Drupal templates to be data-mapping wrappers around your components.
  5. Add Storybook — Set up Storybook to develop and document components in isolation.
  6. Iterate outward — Move to Molecules, then Organisms. Don't try to refactor everything at once.

Conclusion

Component-Based Design isn't just a frontend trend — it's a fundamental improvement in how we architect Drupal themes. By treating your UI as a library of reusable, documented, and testable components, you reduce duplication, prevent style conflicts, enable parallel development, and make your codebase dramatically easier to maintain.

The combination of Drupal's SDC module, .component.yml schemas, and tools like Storybook provides everything you need to adopt this pattern today — without waiting for Drupal to "catch up" with the rest of the frontend world.