
Single Directory Components in Drupal: how theming changes
SDC changes where decisions get made about what a component is. That matters for how your team works.
Drupal 10.1.0 shipped in June 2023 with a new experimental core module: Single Directory Components. Turn it on, and a theme or module can keep a component (the Twig template, a YAML file describing it, and optional CSS and JavaScript) in one folder under components/. Drupal discovers the folder and builds a library for the CSS and JavaScript itself, so there is no entry to add to THEME.libraries.yml.
It sounds like convenience, and it is. The bigger change is architectural. Single Directory Components move the boundary between “what a component is” and “how a component integrates,” and that shift rewires how theming teams and component teams work.
What changed
In traditional Drupal theming, a component is several artifacts. The template lives in templates/. The Sass lives in its own folder and compiles somewhere else. The CSS and JavaScript are registered as a library in THEME.libraries.yml and attached globally from THEME.info.yml or per template with attach_library().
You integrate it by writing a preprocess function. If the component needs data, you fetch it in the preprocess, transform it, and pass it to the template.
This works. It also scatters decisions. The shape of a component lives in three places, and the contract between Drupal and a component (what data it needs, what it outputs) is implicit: documented in prose or not at all.
Single Directory Components change this: a component is a folder. The folder holds the template and a <name>.component.yml file that declares the component’s props as JSON Schema: what it receives, what each value is shaped like, which ones are required. Everything a stranger needs to understand a component is in that folder.
$schema: https://git.drupalcode.org/project/sdc/-/raw/1.x/src/metadata.schema.jsonname: Cardprops: type: object required: - title properties: title: type: string title: Title excerpt: type: string title: Excerpt timestamp: type: string title: Timestampslots: footer: title: FooterAny template can then render the card by its namespaced ID (my_theme:card for a theme called my_theme) and pass the props explicitly:
{{ include('my_theme:card', { title: node.label, excerpt: node.body.summary, timestamp: node.created.value|date('c'),}, with_context = false) }}The consequences ripple outward. The preprocess function stops being the only place a component’s needs are written down, because the schema states them. Developers can open a component folder and read its contract without reading PHP. Tooling can inspect that contract without parsing Twig.
Why this matters for teams
In a traditional Drupal theme, the preprocess layer is where implicit knowledge lives. That is where the conventions are: “use the published date as the timestamp,” or “truncate excerpts to 150 characters.” New developers learn it by reading code. You document it in a readme that falls out of date.
In an SDC theme, that knowledge is explicit in the schema. A card component with props for title, excerpt, and timestamp documents itself. It announces: “I need this data in this shape.” A team member can read the schema and understand the contract. A linter can check against it.
Drupal will also check it, with a catch. Core validates props against the schema through a PHP assert(), so a wrong type throws an error only when assertions are enabled, which is a development setting. On a production server with assertions off, the same mismatch renders without complaint. The schema is a development-time guard, and the theme still has to behave when bad data gets past it.
There is a second catch for themes. Modules must ship a schema for every component. Themes may leave it out unless THEME.info.yml sets enforce_prop_schemas: true. A theme that skips the flag can end up with components that have no contract at all, which brings back the problem SDC was meant to solve.
This shifts who controls the theming conversation. In traditional Drupal themes, the person who understands the preprocess layer is the gatekeeper. In SDC themes, anyone who can read YAML can understand a component’s contract. That is a lower barrier on a multisite platform where several teams build on shared components.
It also changes how you think about integration. If a component’s props are declared in the schema, integrating it into the rest of Drupal becomes a mapping problem: trace where the title comes from (the node title), fetch it, and pass it as the title prop. That mapping can happen in a preprocess function, a field formatter, or the calling template, and the choice is clearer because the component has become simpler.
What I think happens next
Single Directory Components are still finding their foothold in themes. Many production themes have not migrated. Many will run a mix of SDC and traditional templates, which is fine: they coexist. What interests me is what happens when an entire theme is built ground-up as SDC.
I think the shape changes in two ways. First, themes get smaller. A component that states its own contract needs less preprocess code around it, and a theme of fifty SDC components carries less glue than fifty traditional templates with their preprocess logic.
Second, I think components start to cluster differently. In traditional themes, components cluster by where they appear: hero, card, footer. In SDC themes, components cluster by what they do: all things that display a person, all things that display a date, all things that handle navigation. Because the schema is explicit, you can reason about what fits together.
The strongest counter-argument is that this is fine for a well-structured component library but falls apart with institutional variation. A medical school’s component for displaying faculty might need fields that a business school’s does not. Single Directory Components make that explicit (the schema declares the mismatch), but they do not solve it. You either have one component that covers both use cases (and gets complex) or separate components per institution (and lose the benefit of sharing).
I think that is a real tension, and I do not think it is solved yet. What I am watching is whether the Drupal community settles on a pattern for multisite SDC libraries, where variation is sanctioned through configuration rather than through schema inflation. That shift would move SDC from a cleaner way to build themes to a different way to do theming at scale.
If you are building a multisite SDC library and have found a pattern that holds up, I would like to see it.