diff --git a/src/LiveComponent/src/EventListener/DeferLiveComponentSubscriber.php b/src/LiveComponent/src/EventListener/DeferLiveComponentSubscriber.php
index 19928e8bd95..d7e598cfa76 100644
--- a/src/LiveComponent/src/EventListener/DeferLiveComponentSubscriber.php
+++ b/src/LiveComponent/src/EventListener/DeferLiveComponentSubscriber.php
@@ -52,6 +52,7 @@ public function onPreRender(PreRenderEvent $event): void
return;
}
+ $componentTemplate = $event->getMetadata()->getTemplate();
$event->setTemplate('@LiveComponent/deferred.html.twig');
$variables = $event->getVariables();
@@ -66,6 +67,8 @@ public function onPreRender(PreRenderEvent $event): void
$variables['loadingTag'] = $mountedComponent->getExtraMetadata('loading-tag');
}
+ $variables['componentTemplate'] = $componentTemplate;
+
$event->setVariables($variables);
}
diff --git a/src/LiveComponent/src/Test/TestLiveComponent.php b/src/LiveComponent/src/Test/TestLiveComponent.php
index 4e9234e4bea..ac9404a7203 100644
--- a/src/LiveComponent/src/Test/TestLiveComponent.php
+++ b/src/LiveComponent/src/Test/TestLiveComponent.php
@@ -43,7 +43,7 @@ public function __construct(
private UrlGeneratorInterface $router,
) {
$this->client->catchExceptions(false);
- $this->data['attributes']['data-live-id'] ??= 'in-a-real-scenario-it-would-already-have-one---provide-one-yourself-if-needed';
+ $this->data['attributes']['id'] ??= 'in-a-real-scenario-it-would-already-have-one---provide-one-yourself-if-needed';
}
public function render(): RenderedComponent
diff --git a/src/LiveComponent/templates/deferred.html.twig b/src/LiveComponent/templates/deferred.html.twig
index 0705be8e4ee..b55ec440e06 100644
--- a/src/LiveComponent/templates/deferred.html.twig
+++ b/src/LiveComponent/templates/deferred.html.twig
@@ -2,6 +2,11 @@
{% block loadingContent %}
{% if loadingTemplate != null %}
{{ include(loadingTemplate) }}
+ {% else %}
+ {% from componentTemplate import placeholder %}
+ {% if placeholder is defined %}
+ {{ placeholder(__props|default) }}
+ {% endif %}
{% endif %}
{% endblock %}
{{ loadingTag }}>
diff --git a/src/LiveComponent/tests/Fixtures/Component/DeferredComponentWithPlaceholder.php b/src/LiveComponent/tests/Fixtures/Component/DeferredComponentWithPlaceholder.php
new file mode 100644
index 00000000000..e7daba08a2f
--- /dev/null
+++ b/src/LiveComponent/tests/Fixtures/Component/DeferredComponentWithPlaceholder.php
@@ -0,0 +1,18 @@
+
+ {# for row in this.rows ... #}
+
+
+{% macro placeholder(props) %}
+ {%- for i in 1..props.rows -%}
+
+ {%- endfor -%}
+{% endmacro %}
diff --git a/src/LiveComponent/tests/Fixtures/templates/render_component_with_placeholder.html.twig b/src/LiveComponent/tests/Fixtures/templates/render_component_with_placeholder.html.twig
new file mode 100644
index 00000000000..c9b8d4ab6d2
--- /dev/null
+++ b/src/LiveComponent/tests/Fixtures/templates/render_component_with_placeholder.html.twig
@@ -0,0 +1 @@
+
diff --git a/src/LiveComponent/tests/Fixtures/templates/render_deferred_component_with_placeholder.html.twig b/src/LiveComponent/tests/Fixtures/templates/render_deferred_component_with_placeholder.html.twig
new file mode 100644
index 00000000000..ce06cae0f64
--- /dev/null
+++ b/src/LiveComponent/tests/Fixtures/templates/render_deferred_component_with_placeholder.html.twig
@@ -0,0 +1 @@
+
diff --git a/src/LiveComponent/tests/Functional/EventListener/DeferLiveComponentSubscriberTest.php b/src/LiveComponent/tests/Functional/EventListener/DeferLiveComponentSubscriberTest.php
index d243ccba1e8..420fce3b89b 100644
--- a/src/LiveComponent/tests/Functional/EventListener/DeferLiveComponentSubscriberTest.php
+++ b/src/LiveComponent/tests/Functional/EventListener/DeferLiveComponentSubscriberTest.php
@@ -77,6 +77,27 @@ public function testItIncludesGivenTemplateWhileLoadingDeferredComponent(): void
$this->assertStringContainsString('Long awaited data', $div->html());
}
+ public function testItIncludesComponentTemplateBlockAsPlaceholder(): void
+ {
+ $div = $this->browser()
+ ->visit('/render-template/render_deferred_component_with_placeholder')
+ ->assertSuccessful()
+ ->crawler()
+ ->filter('div');
+
+ $this->assertSame('', trim($div->html()));
+ }
+
+ public function testItDoesNotIncludesPlaceholderWhenRendered(): void
+ {
+ $div = $this->browser()
+ ->visit('/render-template/render_component_with_placeholder')
+ ->assertSuccessful()
+ ->crawler();
+
+ $this->assertStringNotContainsString('', $div->html());
+ }
+
public function testItAllowsToSetCustomLoadingHtmlTag(): void
{
$crawler = $this->browser()
diff --git a/src/LiveComponent/tests/Functional/EventListener/InterceptChildComponentRenderSubscriberTest.php b/src/LiveComponent/tests/Functional/EventListener/InterceptChildComponentRenderSubscriberTest.php
index 3dfd77bb270..19cd1740b79 100644
--- a/src/LiveComponent/tests/Functional/EventListener/InterceptChildComponentRenderSubscriberTest.php
+++ b/src/LiveComponent/tests/Functional/EventListener/InterceptChildComponentRenderSubscriberTest.php
@@ -128,7 +128,7 @@ public function testItUsesKeysToRenderChildrenLiveIds(): void
->assertSuccessful()
->assertHtml()
->assertElementCount('ul li', 3)
- // check for the live-id we expect based on the key
+ // check for the id we expect based on the key
->assertContains('id="live-521026374-the-key0"')
->assertNotContains('key="the-key0"')
->visit($urlWithChangedFingerprints)
diff --git a/src/TwigComponent/tests/Integration/ComponentExtensionTest.php b/src/TwigComponent/tests/Integration/ComponentExtensionTest.php
index ff891610c50..aa61825e805 100644
--- a/src/TwigComponent/tests/Integration/ComponentExtensionTest.php
+++ b/src/TwigComponent/tests/Integration/ComponentExtensionTest.php
@@ -14,7 +14,6 @@
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\UX\TwigComponent\Tests\Fixtures\User;
use Twig\Environment;
-use Twig\Error\RuntimeError;
/**
* @author Kevin Bond