Skip to content

feat: restore Altair SVG output as base64-encoded Data URLs#9104

Merged
Light2Dark merged 6 commits intomarimo-team:mainfrom
daizutabi:feat-altair-svg-data-url
Apr 16, 2026
Merged

feat: restore Altair SVG output as base64-encoded Data URLs#9104
Light2Dark merged 6 commits intomarimo-team:mainfrom
daizutabi:feat-altair-svg-data-url

Conversation

@daizutabi
Copy link
Copy Markdown
Contributor

📝 Summary

This PR restores the behavior of outputting Altair SVG charts as base64-encoded Data URLs, following the improvements made to marimo's layout rendering.

Background and Justification

  • Layout Compatibility: The issue where Altair SVG Data URLs failed to render within layout elements (like mo.vstack) reported in Altair SVG doesn't render with layouts #9015 has been addressed in fix: SVG data URL rendering in layout elements #9043. By wrapping SVG Data URLs in <img> tags within mime_to_html, they now render correctly across all layout components.
  • Image Mark Limitation (Altair SVG rendering breaks image marks #9013): While using Data URLs for SVGs prevents external resources (such as mark_image) from loading due to browser security restrictions, this is consistent with the behavior in JupyterLab. It is a limitation of the SVG-as-image format itself rather than a bug in marimo's formatter.
  • Consistency: Converting SVG to Data URLs ensures that Altair's SVG output is handled consistently with other image-based formats (like PNG or JPEG).

Changes

  • Updated marimo/_output/formatters/altair_formatters.py to encode image/svg+xml string responses into base64 Data URLs.
  • Updated tests/_output/formatters/test_altair_formatters.py to reflect the change in the expected output format.

📋 Pre-Review Checklist

  • For large changes, or changes that affect the public API: this change was discussed or approved through an issue, on Discord, or the community discussions (Please provide a link if applicable).
  • Any AI generated code has been reviewed line-by-line by the human PR author, who stands by it.
  • Video or media evidence is provided for any visual changes (optional).

✅ Merge Checklist

  • I have read the contributor guidelines.
  • Documentation has been updated where applicable, including docstrings for API changes.
  • Tests have been added for the changes made.

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 8, 2026

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

Project Deployment Actions Updated (UTC)
marimo-docs Ready Ready Preview, Comment Apr 16, 2026 7:33am

Request Review

@mscolnick
Copy link
Copy Markdown
Contributor

While using Data URLs for SVGs prevents external resources (such as mark_image) from loading due to browser security restrictions, this is consistent with the behavior in JupyterLab. It is a limitation of the SVG-as-image format itself rather than a bug in marimo's formatter.

This would still be a regression and loss of a used feature. Is there a workaround? Or can we opt out when there are external resources?

@daizutabi
Copy link
Copy Markdown
Contributor Author

I understand the concern regarding the regression. However, I believe that restoring Data URL output is the correct path.

  1. Clear User Alternatives: For use cases that require external resources, users can simply use the default renderer, which handles these cases perfectly. Furthermore, if a user needs the SVG file, they can still download it directly via the chart's action menu.

  2. Redundancy of Conditional Inlining: While we could parse the SVG to detect external resources and fallback to inline rendering, doing so would disable "image-like" features such as drag-and-drop and image-specific context menus. Such a fallback would be counterintuitive, as selecting the SVG renderer is a clear indication that the user wants a static, self-contained output.

Indeed, it is essentially a self-contradiction to use a chart that depends on external resources while specifically opting for a static image format. For users who require external resource support, the default renderer remains the appropriate and intended tool.

Personally, I don't think it's right to sacrifice standard and expected image functionality for a specific edge case that is already well-supported by the default renderer and the action menu.

@mscolnick
Copy link
Copy Markdown
Contributor

@daizutabi your arguments make sense. im following up on #9013, to make sure users can actually opt for the default renderer without losing something else.

@mscolnick mscolnick added the enhancement New feature or request label Apr 9, 2026
@mscolnick mscolnick requested review from Copilot and removed request for Copilot April 13, 2026 15:48
@daizutabi daizutabi force-pushed the feat-altair-svg-data-url branch from 1bb27d0 to 5f475b8 Compare April 14, 2026 12:30
@daizutabi
Copy link
Copy Markdown
Contributor Author

Hi @mscolnick,

Following up on our discussion regarding SVG output, I’ve refined the implementation to provide a clean opt-out mechanism for users.

Here are the key points of the updated approach:

  1. The choice between raw <svg> and base64-encoded Data URLs should rest with the user. Providing this choice ensures that marimo remains flexible for specialized use cases.
  2. alt.renderers.enable() accepts arbitrary keyword arguments, which are stored in altair.renderers.options.
  3. I have implemented support for an inline flag.
    • Default: By default (or inline=False), marimo outputs Data URLs to behave like standard images.
    • Opt-out: If a user explicitly sets alt.renderers.enable('svg', inline=True), marimo will output the raw SVG text.

I'd appreciate your thoughts on this!

@daizutabi
Copy link
Copy Markdown
Contributor Author

I want to share a critical productivity feature enabled by Data URL output, especially for those of us working with a browser running inside a VDI (Virtual Desktop Infrastructure) window.

The browser is located entirely within a remote Linux environment, siloed from the local host (typically Windows). Because the browser is "inside" the remote window, standard file downloads are trapped on the remote filesystem.

A standout feature of marimo is that it implements its own "Copy Image" action in the UI. When charts are output as Data URLs, marimo's implementation can push the actual image data to the remote system clipboard.

Why this is essential:

  1. Cross-OS Clipboard Sync: Most VDI software automatically synchronizes image data from the remote clipboard to the local host's clipboard.
  2. Instant Export: By using marimo's "Copy Image" in the remote browser, we can immediately paste (Ctrl+V) the chart into any local host (Windows) application.

Without Data URLs, marimo's custom "Copy Image" feature wouldn't be able to treat the SVG as an image, breaking this vital cross-OS workflow.

@mscolnick
Copy link
Copy Markdown
Contributor

@daizutabi thank you for the thoughtful explanation, this sounds fine to me. I'm currently tied up to dig deeper, so I'm going to tag in @Light2Dark who may have opinions to on the change and implementation

@Light2Dark
Copy link
Copy Markdown
Collaborator

Thanks @daizutabi . From my understanding doing this would be more aligned with how svgs should be presented.

  1. Could you add to the altair_svg_rendering.py smoke test with the new additions.
  2. The issue with the opt-out approach is that marimo users will not know about using inline=True. Is there a way to inform users about this if they hit the issue? Might be hard to detect but could we check if the url has href / xlink:href, and then log?
  3. Would a better name be more suitable, i.e raw_svg=True or data_url=False

@Light2Dark
Copy link
Copy Markdown
Collaborator

Light2Dark commented Apr 15, 2026

Also, something I didn't realize, this PR only affects users who have vl-convert-python installed? I couldn't reproduce the smoke test issue without it. Could you maybe add import vl_convert as vlc at the top of the smoke test.

Seems we are swallowing that error of that package not being installed. Looks like a separate issue.

Light2Dark added a commit that referenced this pull request Apr 15, 2026
## 📝 Summary

<!--
If this PR closes any issues, list them here by number (e.g., Closes
#123).

Detail the specific changes made in this pull request. Explain the
problem addressed and how it was resolved. If applicable, provide before
and after comparisons, screenshots, or any relevant details to help
reviewers understand the changes easily.
-->
We swallowed errors when imports were not installed / configurations
were incorrect when rendering altair charts. This can lead to user
confusion because their settings were not being respected.

I noticed this when testing #9104 and did not have `vl-convert-python`
installed.

I'm hoping this isn't too noisy, I've tried several altair notebooks.
<img width="817" height="39" alt="image"
src="https://github.com/user-attachments/assets/6d553fd2-6a5e-4ca3-82cf-23ab1f331aad"
/>


## 📋 Pre-Review Checklist
<!-- These checks need to be completed before a PR is reviewed -->

- [ ] For large changes, or changes that affect the public API: this
change was discussed or approved through an issue, on
[Discord](https://marimo.io/discord?ref=pr), or the community
[discussions](https://github.com/marimo-team/marimo/discussions) (Please
provide a link if applicable).
- [ ] Any AI generated code has been reviewed line-by-line by the human
PR author, who stands by it.
- [ ] Video or media evidence is provided for any visual changes
(optional). <!-- PR is more likely to be merged if evidence is provided
for changes made -->

## ✅ Merge Checklist

- [x] I have read the [contributor
guidelines](https://github.com/marimo-team/marimo/blob/main/CONTRIBUTING.md).
- [ ] Documentation has been updated where applicable, including
docstrings for API changes.
- [ ] Tests have been added for the changes made.
@daizutabi daizutabi force-pushed the feat-altair-svg-data-url branch from 5f475b8 to 1e1152c Compare April 16, 2026 07:10
@daizutabi
Copy link
Copy Markdown
Contributor Author

I've updated the implementation based on the feedback!

  • Renamed inline to raw_svg: As suggested, I renamed the opt-out option to raw_svg to make its purpose more intuitive.
  • External resource detection: I added a regex check to detect external resources. It correctly ignores internal references (#) and inline data: URLs to minimize false positives.
  • Warning: If external resources are detected when raw_svg is not enabled, a warning is logged to inform the user about the issue and point them toward the raw_svg=True solution.
  • Updated Smoke Tests:
    • Added import vl_convert as vlc to altair_svg_rendering.py to clarify the dependency.
    • Added a test case for alt.renderers.enable("svg", raw_svg=True) to verify the opt-out behavior.

Copy link
Copy Markdown
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.

awesome, thank you!

@Light2Dark Light2Dark merged commit 30cf0a5 into marimo-team:main Apr 16, 2026
40 checks passed
@daizutabi daizutabi deleted the feat-altair-svg-data-url branch April 16, 2026 19:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants