Skip to content

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-88getIdentifier() builds the ID from basename($file, '.component.yml')
  • Reference: /core/lib/Drupal/Core/Theme/ComponentNegotiator.php:72-140doNegotiate(), maybeNegotiateByTheme(), maybeNegotiateByModule()
  • Reference: /core/lib/Drupal/Core/Theme/ComponentPluginManager.php — Discovery implementation
  • Component File Structure
  • Replacing Templates with SDCs