Skip to content

fix: re-render mutable Html children in vstack/hstack on flush#8626

Merged
mscolnick merged 3 commits intomarimo-team:mainfrom
VishakBaddur:fix/spinner-updates-in-layout-containers
Mar 15, 2026
Merged

fix: re-render mutable Html children in vstack/hstack on flush#8626
mscolnick merged 3 commits intomarimo-team:mainfrom
VishakBaddur:fix/spinner-updates-in-layout-containers

Conversation

@VishakBaddur
Copy link
Contributor

Fixes #8618

Problem

mo.status.spinner updates were silently lost when the spinner was placed inside a layout container like mo.vstack or mo.hstack.

Root cause: _flex() eagerly evaluated as_html(item).text for each child at construction time, baking a frozen HTML string into the layout. When spinner.update() later mutated spinner._text and called output.flush(), the parent vstack re-sent its own frozen HTML, the spinner's new state was never visible.

Fix

_FlexContainerHtml now stores live Html references instead of frozen strings, and overrides the text property to call _build_text() on every access. This means each flush() re-reads the current .text of every child, propagating updates through the layout hierarchy.

Testing

Added test_mutable_html_children_update_live to tests/_plugins/stateless/test_flex.py — verifies that mutating a child's _text after embedding it in vstack/hstack is reflected in the parent's .text.

@vercel
Copy link

vercel bot commented Mar 10, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
marimo-docs Ready Ready Preview, Comment Mar 10, 2026 5:46pm

Request Review

@github-actions
Copy link

github-actions bot commented Mar 10, 2026

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@VishakBaddur
Copy link
Contributor Author

I have read the CLA Document and I hereby sign the CLA

@VishakBaddur
Copy link
Contributor Author

recheck

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a rendering bug where mutable Html children (notably mo.status.spinner) embedded inside mo.vstack / mo.hstack would not reflect updates after output.flush(), because parent layouts previously captured (“froze”) child HTML at construction time.

Changes:

  • Update the flex layout implementation to retain live Html child references and rebuild container HTML on each .text access.
  • Add a regression test ensuring that mutating a child Html updates the parent vstack/hstack output.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
marimo/_plugins/stateless/flex.py Reworks _FlexContainerHtml to store live children and dynamically rebuild .text so flushes reflect child mutations.
tests/_plugins/stateless/test_flex.py Adds a regression test covering live updates for mutable Html children in vstack/hstack.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +64 to +67
@property
def text(self) -> str: # type: ignore[override]
"""Re-render children live on every access."""
return self._build_text()
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_FlexContainerHtml.text rebuilds and returns fresh HTML but never updates the inherited Html state (self._text, serialized_mime_bundle, virtual file scanning state). This can make the object’s internal representation/serialization inconsistent with what .text returns (e.g., any code path that serializes __dict__ will still see the initial HTML). Consider updating _text (and any derived fields that are expected to reflect the current HTML) when rebuilding, or overriding the relevant serialization/mime methods to keep a single source of truth.

Copilot uses AI. Check for mistakes.
Comment on lines +28 to +38
def __init__(
self,
style: str,
live_children: list[Html],
child_flexes: Optional[Sequence[Optional[float]]],
) -> None:
self._style = style
self._live_children = live_children
self._child_flexes = child_flexes
super().__init__(self._build_text())

Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_FlexContainerHtml.__init__ calls super().__init__(self._build_text()), and Html.__init__ immediately calls self._mime_(), which in turn reads self.text. Since text is overridden to call _build_text(), the container HTML is built twice during construction. Consider avoiding the extra render (e.g., ensure _mime_() during init doesn’t trigger a rebuild) to keep construction cost proportional to the number/size of children.

Copilot uses AI. Check for mistakes.
Comment on lines -70 to -72
# Only make the wrapper a flex container for nested stacks so their
# flex: 1 and justify work. Leaf content (e.g. mo.stat) fills the
# wrapper when it is a block.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should preserve the original comments

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, restored the original comments. Thanks!

Copy link
Collaborator

@Light2Dark Light2Dark left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think it looks good, pending lint fixes

@VishakBaddur
Copy link
Contributor Author

recheck

@mscolnick mscolnick merged commit 9dd70d2 into marimo-team:main Mar 15, 2026
30 of 40 checks passed
@github-actions
Copy link

🚀 Development release published. You may be able to view the changes at https://marimo.app?v=0.20.5-dev69

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

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Spinner update not working when placed manually inside layout

4 participants