Open
Conversation
Member
Author
|
Checked them...a lot were wrong assumptions but overall solid ideas
|
Rich-Harris
reviewed
Apr 10, 2026
Rich-Harris
reviewed
Apr 10, 2026
| } | ||
|
|
||
| if (options.discloseVersion) { | ||
| // disclose version attach the svelte version to `window` which is not guaranteed |
Member
There was a problem hiding this comment.
is globalThis guaranteed to be available everywhere? I believe so? in which case maybe we just update disclose-version
Member
Author
There was a problem hiding this comment.
I think it is...wonder if we could use the same for $props.id then 🤔
Member
Author
There was a problem hiding this comment.
I'm working on the comments right now but let's skip this to see if we want to do it
Rich-Harris
reviewed
Apr 10, 2026
Rich-Harris
reviewed
Apr 10, 2026
Rich-Harris
reviewed
Apr 10, 2026
Rich-Harris
reviewed
Apr 10, 2026
Rich-Harris
reviewed
Apr 10, 2026
Rich-Harris
reviewed
Apr 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #15470
We're so back!
Finally, thanks to Syntax and SuppCo that sponsored the Custom Renderers initiative, I was able to work full-time for a bit on this (thanks again to my employer, Mainmatter for allowing me to do this).
This, the fact that we recently revisited the direction we were going (that lead to much less code needed), plus the fact that Claude now is decently good at navigating the Svelte codebase allowed me to do a lot of progress (I was also able to re-use part of the code I already had before).
As you can see, this is a big PR, but I tried to split the commits reasonably so that the review process shouldn't be too bad and there wasn't really a way to verify it worked before having built most of it. There are still a few To-dos but luckily, I also have a few more days to work on this (and at this point is in a place where I can also do it in my spare time).
To-dos
How it works
experimental.customRendereris a new compile configuration option. It can be astringor a function that accepts the filename and returns astring. The value should be an NPM package or a module that exports the renderer as its default export. When this option is defined, the compilation output changes a bit (no delegated events, no inlining ofnode.nodeValue="", no customizable select, etc...basically we're doing a lot of optimization that are specific to the DOM which are skipped).The compilation also changes because now the compiled component imports the module you specify, uses
from_treeinstead offrom_html, push the renderer at the beginning of the component and pop it at the end...basically thisgets compiled to
What does a custom renderer looks like? You can have a look at the one I created for testing in
packages/svelte/tests/custom-renderers/renderer.jsto have a more practical example but basically, you can importcreateRendererfromsvelte/rendererand then specify a series of DOM-like operations in your "world".You can then use the return value to "mount" your component
A good custom renderer is crucial to make svelte works properly so we will need to document this correctly (even though I don't expect people to create custom renderers in their day to day and the Svelte team will likely be the primary user of this API).
A few examples of this:
insertassumes that the insertion works like the DOM: if you insert something that already has a parent it should be removed from where it is.insert-ing a fragment means inserting all the children of the fragment in the parent.Limits
A few features of Svelte are designed specifically for the DOM and thus are disabled if you try to compile a component with a custom renderer:
bind:on regular elements is forbidden, since svelte register known DOM events to keep the variables in sync.transition:,animate:,in:andout:are forbidden, since, once again, those use DOM manipulation under the hood.svelte:window,svelte:body,svelte:document,svelte:head... I mean, do I really need to explain why this is forbidden?css: injectedis also forbidden since it appends thestyletag to the documentcreateRawSnippetthrows a runtime error since it relies on thetemplatetag to generate the HTML elements from the string you returnAnother quirk is that you can technically interleave components compiled with different renderers (imagine a DOM component into a Threlte one) but:
beforefunction on the comment that will receive the fragment/element/text that the component is trying to "mount")@rendera snippet compiled with a different renderer from the one that is invoking@render. This means if I'm mounting a DOM component into a Threlte component I can't pass a snippet (unless that snippet is exported from a component compiled without a renderer and imported into the Threlte component)These limitations might change before we merge if we find a way to make them work.
What this PR does?
The main job of this PR is to centralize every DOM operation in
operations.jsas an exported function. This allows the function to check if arendereris available so that it can call the method on the renderer instead of the DOM method. The renderer is also captured in every effect created in the component, since it needs to be "pushed" again before the effect execute. The same is true for boundaries, batches and each blocks.I've also added a somewhat decent test suite that uses a render-to-object renderer that renders the svelte components to...well...an object. This allows the tests to be "similar" to the rest of the test suite (there's even an object-to-HTML string helper to assert the shape of the component) but is executed in a node environment, so every access to DOM API will actually throw.
I've changed some of the validation in place, but there's still a few warnings and errors that don't make sense, which I plan to fix before merging.
A few questions
objectcould help us with the maintainability of the custom renderers API (now Typescript will yell at us if we try to accesselement.valuewithout checking)...but it could make the maintainability of DOM Svelte a nightmare (because now you have to check everything and everywhere). Should we keep it a lie?Extra
To test this out, I (admittedly Claude) built an opentui custom renderer to render svelte component to the terminal...here's a small preview.
Screen.Recording.2026-03-31.at.15.17.13.mp4