SDC Architecture
When to Use
- You need to understand how Drupal discovers and loads components
- You're debugging component registration issues
- You're planning component organization across modules/themes
Decision
Plugin-Based Architecture:
- Component Plugin Manager discovers components at cache rebuild.
- Recursively scans components/ directories in modules and themes for any file matching *.component.yml.
- Generates the component ID as provider:{basename of the .component.yml} — the enclosing directory name plays no part.
- Caches component definitions for performance.
Discovery Locations (all scanned, no precedence between them):
1. Active theme: themes/{theme_name}/components/
2. Base themes: themes/{base_theme}/components/
3. Modules: modules/{module_name}/components/
Components are namespaced by provider, so my_theme:card and my_module:card are two distinct plugins that coexist. Dropping a same-named component into your theme does not take over a module's component — see the replacement rules below and in Replacing Templates with SDCs.
Precedence applies only to replacement. ComponentNegotiator::doNegotiate() first filters all definitions down to those whose replaces key equals the requested ID, then picks a winner among those candidates: a theme in the active theme hierarchy wins (active theme before base themes), and a module-provided candidate is the fallback when no theme claims it. With no replaces declared anywhere, there are no candidates and the requested plugin ID is instantiated directly.
Integration Points:
- Render System: #type => 'component' render element.
- Asset System: Automatic library generation per component.
- Theme System: Component replacement via the replaces directive (themes and modules).
- Template System: include() and embed functions.
Pattern
Pattern: Component ID Format
provider:component-name
Examples:
- olivero:teaser
- radix:button
- my_theme:hero-banner
- my_module:user-card
Common Mistakes
Common Mistake: Expecting nested directories to create namespaces.
WHY: The scan is recursive, so components/atoms/button/button.component.yml is found — but the ID is still provider:button, not provider:atoms:button. Subdirectories are for your own organization only, and two .component.yml files with the same basename under one provider collide no matter how deeply they are nested.
See Also
- Reference:
/core/lib/Drupal/Core/Plugin/Discovery/DirectoryWithMetadataDiscovery.php:79-88—getIdentifier()builds the ID frombasename($file, '.component.yml') - Reference:
/core/lib/Drupal/Core/Theme/ComponentNegotiator.php:72-140—doNegotiate(),maybeNegotiateByTheme(),maybeNegotiateByModule() - Reference:
/core/lib/Drupal/Core/Theme/ComponentPluginManager.php— Discovery implementation - Component File Structure
- Replacing Templates with SDCs