Skip to content

SDC Rendering Decision

When to Use

Use UI Patterns wiring when block types are few and simple — it's the intended use case for ui_patterns_component_per_item and requires no template files. Use Twig include() when you have many block types, need per-instance variants via Layout Builder block configuration, or have components reading 5+ sibling fields.

Decision

If your project... Use... Why
Has 1-3 simple block types, single variant each UI Patterns wiring Native, no template files; matches the formatter's intended use case
Has 5+ block types with varied complexity Twig include() pattern Less repetition, per-instance variants supported, clearer diffs
Needs per-instance variants (LB block configuration via Plus Suite or BlockPropertiesEvent) Twig include() pattern UI Patterns formatters cannot read LB block configuration
Generated by daisyui-sdc-generator from React+Tailwind Twig include() pattern Cleaner generator output, easier to validate, easier to hand-tune
Single SDC with one prop, used in field formatter context UI Patterns wiring The intended use case for ui_patterns_component_per_item
Complex composition (many sibling fields → one component, many slots) Twig include() pattern Avoids 3-level nested entity_field config per slot
Must NOT have per-block-type templates (pure config-only shop) UI Patterns wiring No alternative

Pattern

Trade-offs Matrix

Aspect UI Patterns wiring Twig include() to SDC
Config complexity High — 3-level nesting via entity_field for sibling fields Low — single Twig file per block
Per-block-type maintenance Each block type = deep YAML edit Each block type = ~10-line Twig template
Per-instance variants Hard — requires entity field OR custom source plugin Trivial — configuration.variant\|default('default')
Inline editing (Plus Suite Edit+) Native — fields render through formatters Requires preserving field render arrays ({{ content.field_x }} works; raw strings don't)
Diff readability Poor — dynamic colon-keyed nested keys Good — clear field-to-prop mapping
Learning curve Steep — source plugins, context switchers, scoping Low — one {% include %} syntax

Twig include() Pattern

{# templates/block--inline-block--hero.html.twig #}
{% set variant = configuration.hero_variant|default('default') %}

{% include 'mytheme:hero' with {
  variant: variant,
  heading: content.field_heading[0]['#context'].value,
  subheading: content.field_subheading[0]['#context'].value,
  body: content.field_body|render,
  image: content.field_image,
  cta_primary_text: content.field_cta_primary[0]['#title'],
  cta_primary_url: content.field_cta_primary[0]['#url'],
} only %}

UI Patterns Wiring Pattern

# core.entity_view_display.block_content.hero.default.yml
content:
  field_heading:
    type: ui_patterns_component_per_item
    settings:
      ui_patterns:
        component_id: 'mytheme:hero'
        variant_id:
          source_id: select
          source:
            value: default
        slots:
          heading:
            sources:
              - source_id: 'field_property:block_content:field_heading:value'
                source:
                  type: value
                _weight: '0'
          subheading:
            sources:
              - source_id: entity_field
                source:
                  derivable_context: 'field:block_content:hero:field_subheading'
                  'field:block_content:hero:field_subheading':
                    value:
                      sources:
                        - source_id: 'field_property:block_content:field_subheading:value'
                          source:
                            type: value
                          _weight: '0'
                _weight: '0'
          # ...repeat entity_field nesting for each additional sibling slot

For new projects with multiple block content types and any per-instance variant requirement: default to the Twig include() pattern. Use UI Patterns wiring for genuine field-formatter use cases (a single field rendered as a single component instance, e.g., a tags field rendered as badges).

For projects already running pure UI Patterns wiring at scale and willing to live with the trade-offs: stay consistent. Mixing both patterns inconsistently is worse than either alone.

Common Mistakes

  • Wrong: Defaulting to UI Patterns wiring without considering scale → Right: Works fine for 2 block types; becomes unmaintainable at 10+. Each new block type adds deeply-nested view display config; PR diffs are hard to review.
  • Wrong: Twig include() but passing rendered strings instead of field render arrays → Right: Pass content.field_x (render array), not node.field_x.value (string) — Plus Suite Edit+ inline editing attaches data-edit-plus-* attributes to render arrays only.
  • Wrong: Mixing both patterns within the same block type → Right: Pick one path per block type. Half props from view display, half from template is hard to debug and extend.
  • Wrong: Using UI Patterns wiring for blocks that need LB configuration variants → Right: Pure-wiring path cannot read configuration.{prop} from LB block config. Switch to template pattern or store the variant as an entity field.

See Also