Drupal Single Directory Components (SDC)
How to create and use Single Directory Components in Drupal using Drush and Twig.
Generate a Component with Drush
1
ddev drush generate sdc
Drush will prompt for the component name and machine name, then scaffold the files inside web/themes/custom/mytheme/components/your-component/.
Required Files
Each component lives in its own directory:
1
2
3
4
5
6
components/
└── my-card/
├── my-card.component.yml # required
├── my-card.twig # optional but useless without it
├── my-card.css # optional — auto-loaded by SDC
└── my-card.js # optional — auto-loaded by SDC
SDC automatically loads the component’s CSS and JS — no need to declare them in libraries.yml.
Props and Slots
These concepts mirror JS frameworks like React and Vue.
Slots
A slot is the hole in your component where content goes — structured data, markup, or other components. Slots correspond to paragraph fields and are the content the user sees.
Props
Props are like HTML attributes — key/value pairs passed into the component. The user typically doesn’t see props directly; they control behaviour or styling (e.g. a CSS modifier class).
Component YAML
The .component.yml file is required and must include a props object, even if empty.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# components/my-card/my-card.component.yml
$schema: 'https://git.drupalcode.org/project/drupal/-/raw/HEAD/core/modules/sdc/src/metadata.schema.json'
name: My Card
description: A card component.
props:
type: object
properties:
modifier:
type: string
title: CSS modifier class
slots:
title:
title: Title
body:
title: Body content
Component Twig
1
2
3
4
5
{# components/my-card/my-card.twig #}
<div class="my-card {{ modifier }}">
<h2 class="my-card__title">{% block title %}{% endblock %}</h2>
<div class="my-card__body">{% block body %}{% endblock %}</div>
</div>
Embedding in a Node Template
In node--my-card.html.twig, embed using themename:component-name:
1
2
3
4
5
6
{% embed 'mytheme:my-card' with {
modifier: 'my-card--featured',
} %}
{% block title %}{{ node.label }}{% endblock %}
{% block body %}{{ content.body }}{% endblock %}
{% endembed %}
Passing Field Values via Paragraphs
In paragraphs--my-card.html.twig, pass the rendered field value (not the raw value) — this is Drupal best practice:
1
2
3
4
5
6
{% embed 'mytheme:my-card' with {
modifier: modifier_class,
} %}
{% block title %}{{ content.field_title }}{% endblock %}
{% block body %}{{ content.field_body }}{% endblock %}
{% endembed %}
Using content.field_name (rendered) over paragraph.field_name.value (raw) respects formatters and field permissions.
Useful Modules
| Module | Purpose |
|---|---|
| No Markup | Strips default field wrappers so you control all markup in Twig |
| Twig Tweak | Helper functions for Twig templates |
| SDC Style Guide | Visual browser for all registered SDCs |
| SDC Block | Exposes SDCs as placeable blocks |
| UI Patterns 2.x | Maps fields to component props/slots |
| SDC Display | UI for mapping slots without custom Twig |