Skip to content

[LiveComponent] Return StimulusAttributes from live_action() so it can be spread in a Twig component tag - #3533

Open
Amoifr wants to merge 1 commit into
symfony:3.xfrom
Amoifr:fix/live-action-return-stimulus-attributes
Open

[LiveComponent] Return StimulusAttributes from live_action() so it can be spread in a Twig component tag#3533
Amoifr wants to merge 1 commit into
symfony:3.xfrom
Amoifr:fix/live-action-return-stimulus-attributes

Conversation

@Amoifr

@Amoifr Amoifr commented May 11, 2026

Copy link
Copy Markdown
Contributor
Q A
Bug fix? yes
New feature? no
Issues Fix #3413
License MIT

Today, live_action() casts its return value to a string, which prevents it from being spread into a Twig component tag:

{# ❌ doesn't work: the spread receives a plain string #}
<twig:Table:Head {{ ...live_action('sort', {sortBy: 'id'}) }}>

stimulus_action() already returns a StimulusAttributes object (which is both \Stringable and \IteratorAggregate), so the same call written against it does work. The asymmetry was pointed out by @squrious in the issue discussion.

This PR aligns live_action() with stimulus_action() by returning the same StimulusAttributes object directly. Existing usages remain unaffected because Twig calls __toString() automatically when rendering the value (e.g. <button {{ live_action('save') }}>), and the helper is marked @internal on the PHP side.

The existing LiveComponentRuntimeTest::testGetLiveAction is adapted to cast the value to string for the equality assertion and gains a new assertInstanceOf to lock the return type.

@Kocal

Kocal commented May 14, 2026

Copy link
Copy Markdown
Member

For 3.x, 2.x is not maintained anymore

@Kocal Kocal added Feature New Feature and removed Bug Bug Fix labels May 14, 2026
@Amoifr
Amoifr force-pushed the fix/live-action-return-stimulus-attributes branch from 7ee58ef to 8315cba Compare May 15, 2026 11:02
@Amoifr
Amoifr requested a review from Kocal as a code owner May 15, 2026 11:02
@Amoifr
Amoifr changed the base branch from 2.x to 3.x May 15, 2026 11:02
@Amoifr

Amoifr commented May 15, 2026

Copy link
Copy Markdown
Contributor Author

Done, rebased onto 3.x and re-targeted the PR. Thanks @Kocal! 🙏

@github-actions

Copy link
Copy Markdown
Contributor

📊 Packages dist files size difference

Thanks for the PR! Here is the difference in size of the packages dist files between the base branch and the PR.
Please review the changes and make sure they are expected.

FileBefore (Size / Gzip)After (Size / Gzip)
LazyImage
controller.d.ts 395 B / 257 B Removed
controller.js 904 B / 457 B Removed
Map
abstract_map_controller.d.ts 7.6 kB / 1.49 kB 7.29 kB-4% 📉 / 1.46 kB-2% 📉
abstract_map_controller.js 4.92 kB / 1.4 kB 4.65 kB-5% 📉 / 1.26 kB-10% 📉
Map (Bridge Google)
map_controller.d.ts 10.56 kB / 1.92 kB 10.26 kB-3% 📉 / 1.9 kB-1% 📉
map_controller.js 12.9 kB / 3.19 kB 11.25 kB-13% 📉 / 2.84 kB-11% 📉
Map (Bridge Leaflet)
map_controller.d.ts 9.92 kB / 1.84 kB 9.61 kB-3% 📉 / 1.81 kB-2% 📉
map_controller.js 12.45 kB / 3.36 kB 11.38 kB-9% 📉 / 3.17 kB-6% 📉
Svelte
components.d.ts 200 B / 150 B Removed
components.js 46 B / 69 B Removed
loader.d.ts 435 B / 217 B Removed
loader.js 553 B / 313 B Removed
register_controller.d.ts 384 B / 235 B Removed
register_controller.js 531 B / 303 B Removed
render_controller.d.ts 629 B / 353 B Removed
render_controller.js 1.05 kB / 493 B Removed
Swup
controller.d.ts 1012 B / 360 B Removed
controller.js 1.71 kB / 653 B Removed
TogglePassword
controller.d.ts 896 B / 355 B Removed
controller.js 2.64 kB / 1.07 kB Removed
style.min.css 312 B / 218 B Removed
Turbo
mercure_stream_source_element.d.ts Added 11 B / 66 B
mercure_stream_source_element.js Added 1.06 kB / 484 B
turbo_stream_controller.js 1.11 kB / 545 B 1.47 kB+32% 📈 / 721 B+32% 📈
Typed
controller.d.ts 1.9 kB / 501 B Removed
controller.js 1.8 kB / 638 B Removed

@smnandre

Copy link
Copy Markdown
Member

Will the following code continue to work identically?

<tag {{ 'data-controller="foo bar" ' ~ live_action(...) }}> 

@Amoifr

Amoifr commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

Yes, identically. 👍

Twig's ~ operator stringifies its operands, and StimulusAttributes is \Stringable. The previous code already did return (string) $attributes right before returning, so __toString() produces the exact same string that was returned before. Concatenation (~), interpolation and direct rendering ({{ live_action(...) }}) are all unaffected; only the spread case ({{ ...live_action(...) }}), which used to fail, now works.

Happy to add a test asserting the concatenation output if you'd like it locked down. 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feature New Feature LiveComponent Status: Needs Review Needs to be reviewed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[TwigComponent] How to use live_action() twig function in Twig Components tag?

4 participants