diff --git a/src/LiveComponent/tests/Integration/LiveComponentHydratorTest.php b/src/LiveComponent/tests/Integration/LiveComponentHydratorTest.php
index 4c7c5a419fa..0ae52de620b 100644
--- a/src/LiveComponent/tests/Integration/LiveComponentHydratorTest.php
+++ b/src/LiveComponent/tests/Integration/LiveComponentHydratorTest.php
@@ -274,7 +274,7 @@ public function testCanDehydrateAndHydrateComponentsWithAttributes(): void
$factory = self::getContainer()->get('ux.twig_component.component_factory');
/** @var ComponentWithAttributes $component */
- $component = $factory->create('with_attributes', $attributes = ['class' => 'foo']);
+ $component = $factory->create('with_attributes', $attributes = ['class' => 'foo', 'value' => null]);
$this->assertSame($attributes, $component->attributes->all());
diff --git a/src/TwigComponent/src/ComponentAttributes.php b/src/TwigComponent/src/ComponentAttributes.php
index 3ce8b211ff7..02d0445c1da 100644
--- a/src/TwigComponent/src/ComponentAttributes.php
+++ b/src/TwigComponent/src/ComponentAttributes.php
@@ -30,7 +30,13 @@ public function __toString(): string
{
return array_reduce(
array_keys($this->attributes),
- fn (string $carry, string $key) => sprintf('%s %s="%s"', $carry, $key, $this->attributes[$key]),
+ function (string $carry, string $key) {
+ if (null === $this->attributes[$key]) {
+ return "{$carry} {$key}";
+ }
+
+ return sprintf('%s %s="%s"', $carry, $key, $this->attributes[$key]);
+ },
''
);
}
diff --git a/src/TwigComponent/src/HasAttributesTrait.php b/src/TwigComponent/src/HasAttributesTrait.php
index 3b8f917a85b..ea7877c7688 100644
--- a/src/TwigComponent/src/HasAttributesTrait.php
+++ b/src/TwigComponent/src/HasAttributesTrait.php
@@ -45,8 +45,8 @@ public function mountAttributes(array $data): array
}
foreach ($data as $key => $value) {
- if (!is_scalar($value)) {
- throw new \LogicException(sprintf('Unable to use "%s" (%s) as an attribute. Attributes must be scalar. If you meant to mount this value on your component, make sure this is a writable property.', $key, get_debug_type($value)));
+ if (!is_scalar($value) && null !== $value) {
+ throw new \LogicException(sprintf('Unable to use "%s" (%s) as an attribute. Attributes must be scalar or null. If you meant to mount this value on your component, make sure this is a writable property.', $key, get_debug_type($value)));
}
}
diff --git a/src/TwigComponent/src/Resources/doc/index.rst b/src/TwigComponent/src/Resources/doc/index.rst
index 83166afc391..7629815a567 100644
--- a/src/TwigComponent/src/Resources/doc/index.rst
+++ b/src/TwigComponent/src/Resources/doc/index.rst
@@ -454,6 +454,19 @@ When rendering the component, you can pass an array of html attributes to add:
My Component!
+Set an attribute's value to ``null`` to exclude the value when rendering:
+
+.. code-block:: twig
+
+ {# templates/components/my_component.html.twig #}
+
+
+ {# render component #}
+ {{ component('my_component', { type: 'text', value: '', autofocus: null }) }}
+
+ {# renders as: #}
+
+
Defaults & Merging
~~~~~~~~~~~~~~~~~~
diff --git a/src/TwigComponent/tests/Fixtures/templates/template_a.html.twig b/src/TwigComponent/tests/Fixtures/templates/template_a.html.twig
index e349315440d..213a8a5b83a 100644
--- a/src/TwigComponent/tests/Fixtures/templates/template_a.html.twig
+++ b/src/TwigComponent/tests/Fixtures/templates/template_a.html.twig
@@ -1,3 +1,3 @@
{{ component('component_a', { propA: 'prop a value', propB: 'prop b value' }) }}
-{{ component('with_attributes', { prop: 'prop value 1', class: 'bar', style: 'color:red;' }) }}
+{{ component('with_attributes', { prop: 'prop value 1', class: 'bar', style: 'color:red;', value: '', autofocus: null }) }}
{{ component('with_attributes', { prop: 'prop value 2', attributes: { class: 'baz' }, type: 'submit', style: 'color:red;' }) }}
diff --git a/src/TwigComponent/tests/Integration/ComponentExtensionTest.php b/src/TwigComponent/tests/Integration/ComponentExtensionTest.php
index 8003e8fb417..ca5497e53f7 100644
--- a/src/TwigComponent/tests/Integration/ComponentExtensionTest.php
+++ b/src/TwigComponent/tests/Integration/ComponentExtensionTest.php
@@ -60,7 +60,7 @@ public function testCanRenderComponentWithAttributes(): void
$output = self::getContainer()->get(Environment::class)->render('template_a.html.twig');
$this->assertStringContainsString('Component Content (prop value 1)', $output);
- $this->assertStringContainsString('