Sdc component development
7. SDC Component Development
When to Use This Section
- You're creating new SDC components from scratch
- You need to define component props, slots, and schemas
- You're adding JavaScript to components
7.1 Component YAML Schema
Pattern: Complete Component Schema
File: components/COMPONENT/COMPONENT.component.yml
$schema: https://git.drupalcode.org/project/drupal/-/raw/10.1.x/core/modules/sdc/src/metadata.schema.json
name: Component Name
status: stable
description: Detailed component description
group: Component Group
props:
type: object
required:
- required_prop
properties:
required_prop:
type: string
title: Required Property
description: This prop is required
optional_prop:
type: string
title: Optional Property
default: 'default value'
enum_prop:
type: string
title: Enumerated Property
enum:
- option1
- option2
- option3
default: 'option1'
boolean_prop:
type: boolean
title: Boolean Property
default: false
array_prop:
type: array
items:
type: string
title: Array Property
default: []
drupal_attribute:
type: Drupal\Core\Template\Attribute
title: Drupal Attributes
slots:
slot_name:
title: Slot Title
description: Slot description
Decision Table: Prop Types
| Data Type | YAML Type | Example Use |
|---|---|---|
| String | type: string |
Text, IDs, URLs |
| Boolean | type: boolean |
Flags (disabled, active) |
| Number | type: number |
Counts, measurements |
| Array | type: array |
Lists, utility classes |
| Enum | enum: [...] |
Fixed options (size, color) |
| Drupal Attribute | type: Drupal\Core\Template\Attribute |
HTML attributes |
Common Mistakes
- Missing $schema reference — Enables validation
- Not setting defaults — Props without defaults require values
- Wrong type definitions — Follow JSON Schema spec
- Not documenting props — Use
titleanddescription
See Also
- 7.2 Props and Slots
- Official SDC Schema: https://git.drupalcode.org/project/drupal/-/raw/10.1.x/core/modules/sdc/src/metadata.schema.json
7.2 Props and Slots
Pattern: Props vs Slots Usage
Props:
props:
type: object
properties:
title:
type: string
title: Title
color:
type: string
enum: ['primary', 'secondary']
Twig:
<h2>{{ title }}</h2>
<div class="alert alert-{{ color }}">...</div>
Slots:
slots:
header:
title: Header
body:
title: Body
footer:
title: Footer
Twig:
{% if slots.header %}
<div class="component__header">
{{ slots.header }}
</div>
{% endif %}
<div class="component__body">
{{ slots.body }}
</div>
Decision Table: When to Use
| Feature | Props | Slots |
|---|---|---|
| Simple values | ✅ Use props | ❌ |
| HTML content | ❌ | ✅ Use slots |
| Component composition | ❌ | ✅ Use slots |
| Configuration | ✅ Use props | ❌ |
| Nested components | ❌ | ✅ Use slots |
Common Mistakes
- Using props for HTML — Use slots instead
- Not checking slot existence — Always use
{% if slots.SLOT %} - Confusing prop and slot syntax — Props =
{{ prop }}, Slots ={{ slots.slot }} - Missing required props — Define in
required: []array
See Also
7.3 SCSS Scoping
Pattern: Component SCSS Scoping
Best Practice: BEM Methodology
@import '../../src/scss/init';
// Component block
.my-component {
// Use Bootstrap variables
padding: $spacer;
background: $white;
border: 1px solid $border-color;
// Element
&__header {
font-weight: $font-weight-bold;
margin-bottom: $spacer * 0.5;
}
&__body {
color: $body-color;
}
// Modifier
&--highlighted {
background: tint-color($primary, 90%);
border-color: $primary;
}
// Responsive
@include media-breakpoint-up(md) {
padding: $spacer * 2;
}
}
Decision Table: Scoping Strategy
| Scope | Pattern | Example |
|---|---|---|
| Component | .component-name |
.card-content |
| Element | .component__element |
.card-content__title |
| Modifier | .component--modifier |
.card-content--featured |
| State | .component.is-state |
.card-content.is-active |
Common Mistakes
- Not using BEM — Leads to specificity issues
- Global class names — Scope to component
- Hardcoding values — Use Bootstrap variables
- Not importing _init.scss — Missing Bootstrap foundation
See Also
7.4 JavaScript in SDCs
Pattern: Component JavaScript
File: components/COMPONENT/COMPONENT.js
/**
* @file
* JavaScript for COMPONENT component.
*/
((Drupal) => {
'use strict';
/**
* Attaches COMPONENT behavior.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.myComponent = {
attach(context) {
// Use once() to ensure initialization happens only once
const components = once('my-component', '.my-component', context);
components.forEach((component) => {
// Component initialization logic
const button = component.querySelector('.my-component__button');
if (button) {
button.addEventListener('click', () => {
component.classList.toggle('my-component--active');
});
}
});
},
};
})(Drupal);
Auto-Loading: If COMPONENT.js exists, Drupal automatically attaches it as an asset library.
Common Mistakes
- Not using Drupal.behaviors — Required for Drupal integration
- Not using once() — Prevents duplicate initialization
- Not scoping to context — Breaks AJAX compatibility
- Missing 'use strict' — Best practice for modern JS
See Also
- Drupal JavaScript API: https://www.drupal.org/docs/drupal-apis/javascript-api