Skip to content

Document (and possibly improve) variable scope of override blocks in {% embed ... only %} #4844

Description

@andy-blum

Summary

When overriding a block inside {% embed ... only %}, the block body executes in the embedded template's scope, not the calling template's. So variables available at the call site aren't available inside the block unless explicitly passed via with. This is surprising for component development, and as far as I can tell it isn't documented.

Minimal reproduction

{# component.twig #}
{% block content %}{% endblock %}
{# page.twig #}
{% set msg = 'hello' %}
{% embed 'child.twig' with {} only %}
  {% block content %}{{ msg }}{% endblock %}  {# msg is undefined #}
{% endembed %}

Remove only and it renders hello, which confirms the behavior is specifically the context-trimming of only — the override block resolves msg against the embedded scope, which only has emptied.

Why it's surprising

The block is written at the call site, but it runs in the embedded template's context. That write/run split is not terribly obvious, and it's the piece the embed docs don't currently explain.

Based on the related issues linked below, I understand this is intentional, but I'm flagging that it's undocumented and ergonomically rough for a growing use case.

Motivation: Drupal's Single Directory Components

Drupal's Single Directory Components build on embed, giving components "props" (the with mapping) and "slots" (blocks) — a model very similar to React or web components. The SDC docs explicitly recommend only "to avoid unexpected side-effects," and Drupal theme contexts are large, so only is the norm. The result is that slot content can't see call-site variables unless they're also threaded through props, which is unexpected coming from other component systems.

Ask

  1. Docs: Could the embed page state explicitly that override blocks execute in the embedded template's scope, and that only therefore excludes call-site variables from those blocks?
  2. Design (open question): Is there any way to alleviate this perceived quirk?

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions