-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature: render HTML without hydration #14337
Comments
I like the idea, but I don't really get how the proposed API would work. If I understand correctly, <script>
let { children } = $props();
</script>
{@render.static children?.()} would make more sense. In that case, server generated code would be exactly the same as This approach could allow for partial hydration/server components to be somewhat possible in Svelte. |
@Azarattum
Partial hydration offers very little to no benefit either – it just moves work, it doesn't avoid it. |
I think I still don't quite understand what you mean. Let's take an example: <script>
let count = $state(0);
</script>
{#snippet example()}
<button onclick={() => count++}>Count: {count}</button>
{/snippet}
{@render example()} The generated server code for this is: import * as $ from "svelte/internal/server";
export default function App($$payload) {
let count = 0;
function example($$payload) {
$$payload.out += `<button>Count: ${$.escape(count)}</button>`;
}
example($$payload);
} Which means that Now, let's look at the client code generated with import * as $ from "svelte/internal/client";
var on_click = (_, count) => $.update(count);
var root_1 = $.template(`<button> </button>`);
export default function App($$anchor) {
const example = ($$anchor) => {
var button = root_1();
button.__click = [on_click, count];
var text = $.child(button);
$.reset(button);
$.template_effect(() => $.set_text(text, `Count: ${$.get(count) ?? ""}`));
$.append($$anchor, button);
};
let count = $.state(0);
example($$anchor);
}
$.delegate(["click"]); This code is what make the component interactive. Now, let's imagine how the generated code would work for import * as $ from "svelte/internal/server";
export default function App($$payload) {
let count = 0;
function example($$payload) {
$$payload.out += `<button>Count: ${$.escape(count)}</button>`;
$$payload.out += '<!--end-marker-->';
}
example($$payload);
} and on the client (assuming unused variables and functions have been tree-shaken): import * as $ from "svelte/internal/client";
var root = $.server_template(`<!--end-marker-->`);
export default function App($$anchor) {
const example = ($$anchor) => {
var server_template = root();
$.append($$anchor, server_template);
};
example($$anchor);
} In this case P. S. |
How does the compiler know that when compiling that component that it’s only referenced from a render.static - the hydration flag would still be in the bundle because of other code, so tree shaking can’t do this for you. Also, the attachment of event listeners needs ro happen otherwise it won’t be interactive, no? |
A component doesn't need to know how it's referenced. There is no hydration flag. So the {#snippet example()}
<ServerOnlyComponent/>
{/snippet}
{@render.static example()} Since instead of: import * as $ from "svelte/internal/client";
import ServerOnlyComponent from "./ServerOnlyComponent.svelte";
export default function App($$anchor) {
const example = ($$anchor) => {
ServerOnlyComponent($$anchor, {});
};
example($$anchor);
} I would generate: import * as $ from "svelte/internal/client";
import ServerOnlyComponent from "./ServerOnlyComponent.svelte"; // this will tree-shake
export default function App($$anchor) {
const example = ($$anchor) => {
var server_template = $.server_template(`<!--end-marker-->`); // it could be just $.server_template() if the marker is static
$.append($$anchor, server_template);
};
example($$anchor);
} or even import * as $ from "svelte/internal/client";
import ServerOnlyComponent from "./ServerOnlyComponent.svelte"; // this will tree-shake
export default function App($$anchor) {
$.server_template($$anchor);
}
|
What I'm saying is that it's possible to have those characteristics without introducing a new API. However, that's unrelated to this PR. |
How would we do that exactly? The closest thing we have right now is: {@html import.meta.env.SSR ? render(someComponent).body.replaceAll("<!---->", "<!--~-->" : " "} And it has 2 major downsides. The I agree that this would be out of scope for this PR. Though as it was pointed out here #14323 (comment) , this PR in its current form wouldn't solve the #14323 . |
It's not available right now, but it's on our roadmap for next year.
It would solve part of the problem around hydration markers, but agreed it won't solve it all. It might be better to have these discussions on that issue rather than this PR though. |
We've decided against introducing a new server API for now. |
It can be beneficial to render HTML from Svelte without the need to handle hydration – for things such as email content, CMSs, RSS feeds and other such cases.
Rather than being an option we pass in to
render
fromsvelte/server
, we should likely use another API here to enable future compatibility with possible async functionality, thus making this an API you shouldawait
(similar to what React does for this) as without hydration, showing async loading content would be useless. This change would meant that await blocks would no longer show their pending states and instead wait until the promise resolves.For this I propose
renderStaticHTML
but maybe there's a better name out there.The text was updated successfully, but these errors were encountered: