Skip to content
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

Partial hydration #1390

Open
kevmodrome opened this issue May 9, 2021 · 35 comments
Open

Partial hydration #1390

kevmodrome opened this issue May 9, 2021 · 35 comments
Labels
feature / enhancement New feature or request size:large significant feature with tricky design questions and multi-day implementation
Milestone

Comments

@kevmodrome
Copy link

Unsure if this should go here or in an RFC. I'll start here, but write a proper RFC if the scope is too big for a regular issue.

With the somewhat recent feature of disabling JS completely on pages it would be nice to selectively add sprinkles of JS around the page for some progressive enhancement.

Describe the solution you'd like
Svelte's action directive could work very well for this as. I briefly mentioned the idea to @pngwn and he mentioned that the life cycle hooks/methods wouldn't be usable (as we don't know when something is updated or destroyed. I think for a first version this would be fine - we would simply ignore the update and destroy functions. I don't this would be a big loss since you would lose the context of the page if you navigate away from it anyway, I think (or can you disabled JS but have the router still be enabled?).

Describe alternatives you've considered
An alternative way of doing this would be how Elder.js does it - a preprocessor that looks through your pages and finds any that are marked with some prop (sveltekit:partial?) and bundles that separately, injects that in some intersectionObserver solution. This would allow the usage of full components.

How important is this feature to you?
I can live without it, but partial hydration is 👌 and would give us a nice and easy way to enhance our static pages with whimsy and excitement! 👏

@pngwn
Copy link
Member

pngwn commented May 9, 2021

or can you disabled JS but have the router still be enabled?

You can indeed do this. So we would at least need to do some cleanup. A router navigation event would probably be a good moment to clean up existing actions in the context of a static page.

I think this is an obvious request and one we are bound to get sooner or later. Very few people will want zero javascript but many would like just the tiniest amount.

The problem is that kit would have to parse the component to support something along these lines, a territory that firmly belongs to svelte.

I almost feel like this would make more sense as a 'mode' of the svelte compiler.

Obviously, the real solution to this is to support partial hydration. I don't know if this feature request would be a reasonable half measure or if efforts are better invested in exploring partial hydration in its entirety.

@benmccann
Copy link
Member

Partial hydration is basically solving a performance issue by helping users get smaller bundles and faster code execution. There's several open issues around improving Svelte performance. I think it'd be interesting to see how far we can get without partial hydration as I expect we can get pretty phenomenal performance without having to decide on API changes which would take more effort to agree on.

Probably the most impactful would be to improve Svelte's hydration implementation to be more performant (sveltejs/svelte#4308). There was an optimization added in Svelte 3.38.0 that was reverted in Svelte 3.38.2 due to a bug. I'd hope that there's a path to getting that optimization checked back in without the bug present since the original approach was agreed upon and there's been a unit test added for the bug. I just commented there suggesting a potential solution. The optimization moved the Google Page Speed score for my site from 92 to 99, so I think at least for some cases that would be a very meaningful improvement that wouldn't require any API changes.

Finally, while it's not related to hydration at all, there are a couple open performance-related PRs and Rich's SSR PR improves SSR performance by 2-3x and is pretty close to being able to be checked in.

@pngwn
Copy link
Member

pngwn commented May 11, 2021

I'm all for the performance improvements in Svelte but if my content is mostly static Svelte will never come close to some selective enhancement with JS, regardless of how good the hydration performance gets because in those case it is doing unnecessary work.

I think since the static and router less options exist in svelte kit, this is a request we will get more and more. A blog, for example, is probably mostly static but users might want a pop out menu. And they might want to write that code in svelte, which is understandable. No performance improvement in svelte is going to make treating all of that static content as some thing to be hydrated even in cases where Svelte optimises the code by setting inner html, having static html as strings in JS when it is unnecessary is a bad idea.

I'm not saying this proposal is the correct way of doing it, but I don't think avoiding some kind of selective hydration is really a solution either.

@nickreese
Copy link

Just as a note for the use:action directive. This was the bug I hit with regards to implementing it on Elder.js. sveltejs/language-tools#339

This could be solved if it was an official svelte directive though.

@SteveALee
Copy link

SteveALee commented Jul 15, 2021

I'm pleased to see a growing interest in partial Hydration for Progressively Enhanced SSG content. AstroJS and petite-vue are the newest 'frameworks' exploring this. The term 'island architecture' seem to be growing in usage for this; ie there's no top-level app component

I recently explored using 11ty and AlpineJS in an interactive music Journal.

I'd personally love a svelte PH solution for a project targetting users in parts of Africa where connectivity is poor and older devices are used.

@Madd0g
Copy link

Madd0g commented Nov 10, 2021

The term 'island architecture' seem to be growing in usage for this

nice, I like that term.

I have server rendered pages right now with a single very simple interactive component. Would love to be able to load only that specific tiny interactive JS in the client, instead of the entire page + metadata.

@theurgi
Copy link

theurgi commented Jan 24, 2022

I'm not saying this proposal is the correct way of doing it, but I don't think avoiding some kind of selective hydration is really a solution either.

Watching the developments in this space, it's pretty obvious that partial hydration/islands architecture is going to set the new standard in terms of performance.

Where as Astro starts with this as a foundation and is working their way up to a more comprehensive framework by developing dynamic component SSR, React is taking a retrofit approach with Server Components.

Shipping the least amount of JS possible to the client seems like a forgone conclusion. Which ever framework (whether existing or new) can best marry this with the declarative component architecture that's come to dominate developer's mental model, will win in the long run IMO—I hope it's obvious in what sense I mean "win".

Of all the frameworks I've worked with, I find Svelte to be the most intuitive and enjoyable, but I wouldn't hesitate to migrate to another framework for the not insignificant performance gains afforded by this. I'd say it's almost obligatory for competitive e-commerce sites to prioritize this over developer experience.

I'm curious if there are any priority updates, especially now that Svelte has backing from Vercel:

image

@theurgi
Copy link

theurgi commented Jan 24, 2022

I suppose that answers my main question in regards to where Svelte is currently at, but...

Now put in one built with the island architecture - I bet it won't beat SvelteKit.

Well, most recently, I read this: SvelteKit vs. Astro

I fully agree with consulting actual metrics over buzzwords and in this particular case the performance gain of Astro is probably negligible for most applications. Based on this alone, I'd even prefer SvelteKit because Astro currently has some limitations where it's hard to determine whether or not they'd present a costly or even impassible challenge deeper into the development cycle.

But if I need to commit to a framework for a large scale application (and I do) and performance is a top priority, and to migrate frameworks down to road is not really an option—this remains a relevant question for me especially considering the way Svelte apps scale horizontally (you know, the whole inflection point thing: JavaScript Framework TodoMVC Size Comparison).

My assumption is that selective hydration would buy Svelte a significant amount of headroom with, if not completely solve, its horizontal scaling issue.

I was hoping that, now that Rich rubs shoulders with all the Vercel engineers who live on the bleeding edge of React, they would convince him of this magic. I wan't to choose Svelte, but all things considered I may have to take my chances with React/Preact.😔

@amardeep
Copy link

I was also hoping that svelte would support partial hydration.

I think now I would watch either Marko or Qwik

Sveltekit might be really fast, but when I am developing something which has lots of content, and little interactivity, I prefer a framework which will just send the html as is without also sending the code to render it and the data again in the js. I understand it is a subjective opinion, but I think the web would be a better place without all the unnecessary javascript being sent with everything.

@SteveALee
Copy link

SteveALee commented Jan 24, 2022

@amardeep I'm with you totally as the best approach. I recently used 11ty and alpine for such an approach (https://musicpracticetools.net/)

The advantage about being explicit is it keeps Progressive Enhancement in mind.

However, last time I looked at the code Svelte generated (probably back when v3.0 launched) it generates the minimum dynamic content through javascript and deploys a minimal 'framework' runtime payload.

In other words, Svelte is very close to an implicit automatic island architecture. I assume Sveltekit builds on this; it does let you specify completely staic on a per route basis.

I may be wrong though.

@khromov
Copy link

khromov commented Jan 24, 2022

@SteveALee

However, last time I looked at the code Svelte generated (probably back when v3.0 launched) it generates the minimum dynamic content through javascript and deploys a minimal 'framework' runtime payload.

I was also curious about whether Svelte compiler could optimize static components but unfortunately it seems that the entire DOM is still present in the JavaScript bundle.

I spun up a new SvelteKit site with the following index component:

<h1>Welcome to SvelteKit</h1>
<p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation!</p>

I then built it via @sveltejs/adapter-node and ran the resulting build.

An index.svelte-xxx.js chunk was served that contained this:

import {S as K, i as y, s as V, e as v, t as c, k as W, c as u, a as _, h, d as l, m as b, b as j, g as k, J as o, K as x} from "../chunks/vendor-f0095a1c.js";
function q(E) {
    let e, d, r, a, m, s, f, p;
    return {
        c() {
            e = v("h1"),
            d = c("Welcome to SvelteKit"),
            r = W(),
            a = v("p"),
            m = c("Visit "),
            s = v("a"),
            f = c("kit.svelte.dev"),
            p = c(" to read the documentation!"),
            this.h()
        },
        l(t) {
            e = u(t, "H1", {});
            var i = _(e);
            d = h(i, "Welcome to SvelteKit"),
            i.forEach(l),
            r = b(t),
            a = u(t, "P", {});
            var n = _(a);
            m = h(n, "Visit "),
            s = u(n, "A", {
                href: !0
            });
            var S = _(s);
            f = h(S, "kit.svelte.dev"),
            S.forEach(l),
            p = h(n, " to read the documentation!"),
            n.forEach(l),
            this.h()
        },
        h() {
            j(s, "href", "https://kit.svelte.dev")
        },

So unfortunately, Svelte can't currently optimize away even simple cases like this where there is no interactivity - everything gets sent twice over the wire, first as HTML and then as JS.

@dummdidumm
Copy link
Member

dummdidumm commented Jan 24, 2022

So unfortunately, Svelte can't currently optimize away even simple cases like this where there is no interactivity - everything gets sent twice over the wire, first as HTML and then as JS.

Not true. If you set both hydrate and router to false, no JavaScript will be created for that page. See the docs for more info.

@benmccann
Copy link
Member

benmccann commented Jan 24, 2022

The TodoMVC and Vue articles are both extremely misleading and inaccurate because they ignore code splitting. E.g. the TodoMVC article says when you have an application five times the size of TodoMVC then you'll start to get worse performance with Svelte. But SvelteKit loads only the JS necessary for a page - not the entire application's JS. So really you would need a single page that is larger than five applications. I've never seen a real application get anywhere even close to that. Those articles were both written by people promoting other frameworks and, while I respect the authors, they lead to such wildly inaccurate and biased conclusions

I do agree partial hydration would be a nice enhancement and it's something we'll add one day, but having it will probably add about one point to your Google Page Speed Score (it'd be hard for it to do better since most apps are already scoring around 99-100). Meanwhile, nearly every virtual DOM application I've seen scores in the 60s or 70s. If you added partial hydration to these apps most of them would be nowhere close to performing as well as a SvelteKit app, so it really is important to look at the whole picture

Svelte absolutely does send unnecessary JS over the wire today. But in practice, it still sends far less unnecessary code than almost everything else out there including most non-Svelte island architectures. We would like to improve hydration, but we also want to take our time to do it right because this is a feature that will probably be implemented at least partially in Svelte core and won't be easy to change once it's been released. We'll likely consider it post SvelteKit 1.0 as focus shifts back to Svelte core. And if you're already on SvelteKit you will one day get those improvements for very little additional work

@deklanw
Copy link

deklanw commented Jan 28, 2022

@benmccann

Meanwhile, nearly every virtual DOM application I've seen scores in the 60s or 70s.

All of the popular frameworks use VDOMs. Inexperienced devs gravitate to them and bloat their code with npm-based-development. It's not hard to find performant VDOM apps. For example, despite using one of the more bloated VDOM libraries (React), this Remix demo is far above 60s and 70s.

Now put in one built with the island architecture - I bet it won't beat SvelteKit.

The TodoMVC and Vue articles are both extremely misleading and inaccurate because they ignore code splitting. E.g. the TodoMVC article says when you have an application five times the size of TodoMVC then you'll start to get worse performance with Svelte. But SvelteKit loads only the JS necessary for a page - not the entire application's JS.

I've been investigating frameworks for making a blog. Making a blog with small interactive elements changes this performance calculus.

As a quick and dirty benchmark I made a long blogpost with a single interactive Counter component with Sveltekit + MDSveX, Astro, and Iles. (Slinkity would also be nice to test, but I didn't). These sites aren't purely identical but it should be good enough. Lmk if I made any mistakes.

Result: they all get 100 in performance with Lighthouse. Great! Let's look closer. For the Counter component, Iles loads the Vue runtime and Astro loads the Preact runtime. Let's see how much JS is sent by each of these. For reference, in all these cases the HTML file is approx 70KB uncompressed, 10KB gzipped.

Framework uncompressed JS (KB) gzipped JS (KB)
Sveltekit+MDSveX 268KB 54KB
Astro 12KB 6KB
Iles 53KB 22KB

Sveltekit+MDSveX is sending 22x more JS than Astro. With gzip it's fortunately only 9x as much. That Sveltekit+MDSveX is on par here is impressive. The work done on optimizing the SSR has paid off, apparently sveltejs/svelte#6204, sveltejs/svelte#6395 But, well-optimized waste is still waste. I think this is what @pngwn was expressing above. Note, this only gets worse the longer the blogpost. I would like to test this further with longer posts, but because of long-outstanding bugs pngwn/MDsveX#358 sveltejs/svelte#4694 Svelte doesn't afford blogposts any longer.

How realistic is the benchmark? Well, IME most dev blog posts are a few paragraphs long. So, maybe this won't be a problem for most users. What about people who do write long posts?

Let's see

Article Word count # DOM elements uncompressed HTML (KB) gzipped HTML (KB)
Benchmark 7893 899 70 10
Gwern article 69.3k 17.5k 1300 321
SSC article (removed comments*) 33k 3.3k 331 97
SSC article (comments in-tact) 135k 15.5k 1600 340

*I remove the eagerly-loaded comments. They comprise most of the page, so removing them simulates 'just blogpost'.

Should these authors just "code split" their longer blog posts? Maybe. If that Gwern article were ported over to Sveltekit+MDSveX I estimate the JS to hydrate would be around 2MB-4MB, uncompressed. In that case, how much would performance degrade as a result of downloading, parsing, and executing all of that JS versus downloading, parsing, and painting all of that HTML? I'm not sure. How would it fare versus Iles and Astro? If it weren't for the aforementioned bugs I would try to estimate that.

Since these performance issues only come up at very long post length, and I would like to use clientside routing with animated transitions, I'll probably move forward with Sveltekit+MDSveX. But, just wanted to lay out my findings here. I agree with the assessment that partial hydration can be considered a future "nice to have", but it's not because Svelte is blowing out the competition already.

@benmccann
Copy link
Member

benmccann commented Jan 29, 2022

Thanks these are useful stats! To be clear, I agree that we should add partial hydration. I just want to be upfront that I don't know that there's a whole lot that will change how it's prioritized because our current thinking is that it'd depend on changes to Svelte core and we want to get Kit 1.0 out the door before shifting focus back to the Svelte internals. For anyone who's interested in seeing partial hydration, probably the best way to help make that happen is to help with Kit 1.0 😉

this Remix demo is far above 60s and 70s.

I got 72, 90, 89, 84, 66 when I tested it: https://pagespeed.web.dev/report?url=https%3A%2F%2Fremix-ecommerce.fly.dev%2Fen%2Fsearch%3Fcategory%3Dcharacters

That averages out a 80. Certainly an improvement over most React sites, but still quite a bit below most SvelteKit sites I've seen - though obviously we don't have a 1:1 comparison

because of long-outstanding bugs pngwn/MDsveX#358 sveltejs/svelte#4694 Svelte doesn't afford blogposts any longer.

Oof. This seems like the first thing we should fix. It looks like there's a PR open for it: Rich-Harris/code-red#64

@deklanw
Copy link

deklanw commented Jan 29, 2022

Thanks these are useful stats! To be clear, I agree that we should add partial hydration. I just want to be upfront that I don't know that there's a whole lot that will change how it's prioritized because our current thinking is that it'd depend on changes to Svelte core and we want to get Kit 1.0 out the door before shifting focus back to the Svelte internals. For anyone who's interested in seeing partial hydration, probably the best way to help make that happen is to help with Kit 1.0 😉

Fair. Partial hydration is non-trivial for full SPAs. Just wanted to share my findings and make the case for real-world relevance. Hope to see Kit 1.0 soon! I may try to contribute 🙂

I got 72, 90, 89, 84, 66 when I tested it: https://pagespeed.web.dev/report?url=https%3A%2F%2Fremix-ecommerce.fly.dev%2Fen%2Fsearch%3Fcategory%3Dcharacters

That's curious. Ran using the site you gave five times and got: 94, 92, 94, 91, 89. I'm never getting anything as low as you got. And, when I run Lighthouse locally I get 99 everytime. That site seems to be a frontend for Lighthouse, so 🤷‍♂️

Oof. This seems like the first thing we should fix. It looks like there's a PR open for it:

Yeah, I saw. Also outstanding for months, unfortunately. Hope it's fixed soon so I can do some more testing

@benmccann
Copy link
Member

@deklanw that issue is hopefully fixed now with the release of Svelte 3.46.4. Would you be able to give it another try and check if it works now? Thanks for alerting us to it. code-red rarely needs any changes, so it's easy to overlook PRs in that repo.

@deklanw
Copy link

deklanw commented Feb 4, 2022

@benmccann Yep, works now! Nice work.

Scaled up the benchmark I did above and put the code and results in a repo. I found out the answer to the question I posed above: no, the performance degradation caused by large HTML files doesn't drown out the performance degradation of SvelteKit's hydration. For the longest blogpost in my testing (with Lighthouse) SvelteKit gets 53, Astro gets 80, and Iles gets 78. The TTI degrades in SvelteKit's case.

@benmccann
Copy link
Member

benmccann commented Feb 4, 2022

Thanks for sharing your findings @deklanw.

The actual page load as a user seeing contents isn't much different for me between the three implementations. Some people I shared this with are reporting SvelteKit actually looks faster for them

Rich pointed out the preload directive is broken on the SvelteKit example (deklanw/interactive-blogs-benchmark#1), so fixing that might improve things in SvelteKit's favor a fair amount. It won't help the Lighthouse score, but will make things look a lot faster to users, which is the more important thing

sveltejs/svelte#3898 would also improve performance a fair amount in this example, but the draft branch a community member made for it is probably too complicated to get into Svelte 3. sveltejs/svelte#2374 and sveltejs/svelte#3760 are similar suggestions

@deklanw
Copy link

deklanw commented Feb 5, 2022

The actual page load as a user seeing contents isn't much different for me between the three implementations. Some people I shared this with are reporting SvelteKit actually looks faster for them

That's true. I agreed in the README that TTI for an interactive blogpost probably doesn't matter. As long as FP/LCP are on par (they are) then it's fine. By the time the user scrolls down to the data viz or whatever it should be interactive.

Thanks for the PR correction. I was focusing on top-level loads because those seem appropriate for blog posts. Haven't tested hydrated page transitioning at all.

I'll check out those optimization PRs you linked. Thanks! I hope the benchmark can be useful in the future if some of those optimizations (or full partial hydration) are implemented.

@benmccann
Copy link
Member

Yeah, the benchmark is helpful. I've been looking deeper and it seems that, with or without partial hydration, there's another opportunity for improvement that the benchmark has uncovered. I filed a new issue for it. Unfortunately, I'm not that familiar with the internals of Svelte core, so I wouldn't be able to look at it myself. My initial thought is that it seems like something relatively fixable in Svelte 3 if anyone is interested in taking a look at it. I imagine that the time-to-interactive should be able to be made way faster for this example

@ryansolid
Copy link

ryansolid commented Mar 6, 2022

@benmccann

The TodoMVC and Vue articles are both extremely misleading and inaccurate because they ignore code splitting. E.g. the TodoMVC article says when you have an application five times the size of TodoMVC then you'll start to get worse performance with Svelte. But SvelteKit loads only the JS necessary for a page - not the entire application's JS. So really you would need a single page that is larger than five applications. I've never seen a real application get anywhere even close to that. Those articles were both written by people promoting other frameworks and, while I respect the authors, they lead to such wildly inaccurate and biased conclusions

The testing method is limited as multiplying an already zipped output reduces the effectiveness of compression and not all code is created equal, not all things are components etc... So there is truth in your concerns, but the scale isn't quite right. What's 5 TodoMVCs + single vendor bundle? ~10kb of js min-zipped. I'm sure you've encountered larger initial page loads. I mean Svelte's Hackernews Demo is initial page load is larger than that and it does basically nothing. When put in that perspective the article covers a mostly reasonable range even considering code splitting.

It's up to you how you want to spin it. But the suggestion this is about promotion is too convenient. I did work to make sure that Preact and React were properly depicted because their official TodoMVCs were bloated. And Preact was the "winner" if there was one. I'm never afraid to include things that would make my framework not look the best, that's how we grow.

Put a site built with SvelteKit into Google's https://pagespeed.web.dev/. You'll get a perfect score or close to it. Now put in one built with the island architecture - I bet it won't beat SvelteKit.

Also, since I'm here. I take this bet. It isn't hard to make examples that showcase this. Again I'm not trying to push some feature Solid has. We don't. But I don't gain anything from not being upfront with it. Better than seen as misleading later.

But to be fair I am not as familiar with SvelteKit as I should be. Can't ever say anything with certainty. So I will remedy this. I think this project will be my next source of focus. Thanks for the inspiration.

@benmccann
Copy link
Member

benmccann commented Mar 8, 2022

Benchmarks

I don't think that you can ignore code splitting and other performance nuances and get a realworld understanding of performance. Looking at performance on a per-page basis is a lot more meaningful in my opinion. I really like @deklanw's demo project as one to focus on because it's the first one I've seen that demonstrates Svelte having meaningfully worse performance on a single page. With that demo we have a good example that's enough for us to make measurable progress, so I don't know if we need to get bogged down on discussing the merits of other performance benchmarks

Hydration background

Svelte does hydration differently than React and other frameworks. Svelte "repairs" the DOM to ensure that the client-side DOM matches what is expected. E.g. if you rendered a date with a timezone, in Svelte it will update that date on the client whereas React won't. However, this also introduces a large performance overhead. In the example that @deklanw's shared we have to create a lot of hydration code to check that every piece of text matches what is expected on the client and repair it if it doesn't. This extra code is where the performance impact is coming from in the Svelte case. Even ignoring the runtime impact of running this code, it increases the size of the JS that has to be sent over the wire

Here's the REPL for @deklanw's example: https://svelte.dev/repl/c91e3cef3fe94b859e26e573f052ef42. The JS output is 4,000 lines. If you turn on hydration it grows to 100,000 lines because of all the repair code.

Ways we can improve performance on @deklanw's demo

Prioritizing

Performance is a high priority. I do think it's helpful to have some conversation around what would be most impactful in terms of improving performance further for a few reasons. E.g. I and other maintainers may be overlooking realworld use cases that we don't see in our own projects and we all may have different estimates of the impact of certain performance changes. I certainly am not dismissing any of the issues raised here, but do want to ensure we have the same understanding to help prioritize. I had no idea that very large Svelte pages were previously crashing, so this thread has already helped improve things by getting sveltejs/svelte#4694 fixed and that wouldn't have happened without all the discussion here!

In terms of this specific issue and the three options:

  • Repair optimizations require no code changes to the user code and would be the easiest to get a PR accepted for because there would be the least debate over what the API should be, etc. It'd probably give only 30-40% of the impact of other changes
  • An option to disable repair mode would require a fair amount of discussion from the maintainers before it could be agreed to. It would not necessarily solve the data serialization issue on its own but would shrink the JS bundle just as much as partial hydration
    • we'd want to consider it in the context of other changes we'd like to make to the Svelte internals
    • we'd want to introduce dev mode warnings to let people know if their client-side and server-side HTML differ and a repair is not happening
    • it might be somewhat difficult to implement as it requires a lot of knowledge of the Svelte internals. There was a prior PR that implemented it without an option (Fast hydration & FIX iframes hydration svelte#4309) and could be used as a starting basis. It's possible that an initial implementation could be a partial implementation that only turns off repairing static HTML to get most of the win and potentially make the implementation easier
  • Partial hydration is the hardest to use from a user standpoint and would probably require the most discussion from the maintainers as it's the most user impacting change

@ryansolid
Copy link

ryansolid commented Mar 8, 2022

@benmccann Sounds good. Not that you are asking my opinion, but as I said I'm here, so I will give it.

I think repair comes with the territory as the default when you don't have a VDOM and you are wiring up reactive execution. I haven't witnessed it being that detrimental compared to React but I've only looked at large examples that have been bottlenecked elsewhere. I didn't realize others didn't do that. I feel that they may have at some point in the past?

I think in general that is an interesting question and is something to discuss. I think it is particularly interesting where things are heading because it is sort of discussion because it serves as a basis for other decisions, especially if you look along partial/resumable hydration patterns. We actually talked about this at length when working on Marko 6, because I was very much originally in the camp that the client should be the source of truth. However, the more you move pieces to the server that don't update the more likely you are to have inconsistency. Once you start making decisions, like don't run this expression in the browser because you already did on the server you can no longer rely on the behavior that the client is going to fix it in all places. Ultimately the only option was to treat the server as the initial source of truth, the more you offload (which is inevitable) the more this becomes the case. So I think this is good thinking.

As for Partial Hydration, there are different ways to attack this for Svelte. Compiler gives you lots of options. As far as I can tell the proposal is basically as a way to enrich static pages and that is fair. People already revel at reduction of 50-80% of JS component code size, but I do think there is a benefit beyond the JS hydration that come here.

Data serialization savings. Something fetched on the server that can not be re-fetched doesn't need to be serialized on whole if it would be rendered into the HTML only on the server. Any data interacting with client components would need to be present for hydration but what is also interesting about partial hydration is the potential of partial data serialization. Not an all or nothing thing. This actually can reduce page size, server rendering, and hydration time in one swoop.

Beyond that if there is an interest, I believe there are other ways to attack this without the DX concerns. Svelte is a compiler after all. It comes down to the fact partial hydration and progressive hydration don't necessarily need to be conflated. However, it is a very large investment for something probably tertiary to the main goals.

@JordanShurmer
Copy link

JordanShurmer commented Jun 29, 2022

Personally I would love (something like) the partial hydration being described here.

For reference here are some other frameworks that do various forms of "partial hydration" or "resumability" or "islands of execution"

I'm not proposing that any of these approaches is the right one, or that Svelte should follow any of them. I'm just listing some examples for reference. Anyone looking to understand the concept more can read the various perspectives on it, and Svelte can potentially learn the pros/cons from the different approaches already happening.


EDIT: added another framework

@keehun
Copy link

keehun commented Jul 10, 2022

I would like to add to this discussion a non-performance reason to add partial hydration.

Reducing the need to spend resources on API calls for static content that is only updated at build time. Static blogs/encyclopedia/dictionary/etc, for example. The API doesn’t need to be a secret or hidden, but as you can see, there’s a lot of reduction in traffic we can achieve if the API is only hit during build-time. This would empower small creators like myself to bootstrap new startups or large-audience hobby projects without much or any hosting costs.

Svelte is by far the most ergnomic and intuitive framework I’ve ever used, and at least for what I need, partial hydration is the last big piece.

I’ve tried the trick where you import browser from $app/env and short circuit load() when in the browser. While it works as I expect, it throws errors into console as the page attempts to hydrate and is missing that data it would have fetched in the load() call.

Right now, my solution is to run a python script that is executed before sveltekit build which will pull all the JSON files from the API. These files get put in the static directory. In essence these files serve as the API “server” so visitors will never actually hit my API services. This is working well for now, but I would like to see if I can skip this middle step.

The only other way I can imagine implementing this is by utilizing something like jinja2 or another “code generation” tool that would read the API and actually generate Svelte code which then would be built by Svelte.

Both of these solutions fall far from the tree of what I would consider a good, long-term solution. SvelteKit and pre-rendering already does everything I want except for that “hydrate everything again in the browser”.

Thank you for your time.

@Lucasmiguelmac
Copy link

Disclaimer: I'm fully ignorant on the challenges involved in releasing this feature, let alone the viability of what I'm going to propose next.

AFAIK, this feature requires figuring out two parts:

  • The syntax: figuring out how to indicate what components should keep hydrated, how should they be hydrated and what data sources if any should be fetched for the remaining components that don't require hydration (i.e: a simle image list).
  • The processing: grabing this input with said syntax and effectively rendering the output HTML+CSS+JS files.

What I thought would be a good idea in order to speed up the release of this feature is to take on the syntax part first and release that ASAP, before the processing part.

That is, in contrast to waiting for the full feature to be developed, aim to release the frontend part of it so:

  1. As soon as we have this not-so challenging feature syntax, the community can start building right off the "partial-hydration-able server-side renderer" on the side . Speeding-up the time we can start using this.
  2. We would no longer tie developers to a Node.js server, and instead let every SvelteKit user use a custom backend solution in a microservice fashion.
  3. SvelteKit maintainers can still take their time to work on a fully opinionated backend for SvelteKit without tying us users to wait for it.

@benmccann benmccann added the size:large significant feature with tricky design questions and multi-day implementation label Aug 1, 2022
@benmccann benmccann added the feature / enhancement New feature or request label Jan 20, 2023
@benmccann benmccann modified the milestones: soon, later Jan 24, 2023
@vettloffah
Copy link

My site is having an issue with the drop-down nav not being usable for quite some time while waiting for the page to hydrate. On a fast connection, usually by the time the user clicks the nav, it works fine, but on a slow connection it can take up to over 1 minute while the page is loading / hydrating.

I feel like this would be a great use-case for partial hydration. If anyone else has any suggestions about how to remedy the issue without partial hydration please let me know.

@benmccann
Copy link
Member

I've been thinking about this a lot as Svelte 5 has been in development. There are potentially some clever things we could do automatically in some followup release like figuring out that a component is only ever called with constant values and don't need to be hydrated. For example, your footer content may never change and so potentially we could skip hydrating it.

But larger picture, it's quite tricky to figure out if there's really much benefit that partial hydration can provide for the general case once we're on Svelte 5. Svelte 5 really shrinks the size of the JS, which makes the cost of loading it lower and reduces the benefit of partial hydration. The page contents are only duplicated on the load of the first page. For all subsequent pages you need the content in the JS for SvelteKit's hybrid rendering, so you can't really get rid of it. You could use something like Astro+Svelte to avoid the duplication by creating an MPA and perhaps SvelteKit could have an MPA mode. However, while that could help performance in selected cases, I'm not sure whether that's actually best for performance or not for the general case. The way that an MPA like Astro works is that subsequent page views reload the whole page. It's better for initial page load since there's less JS, but it's worse for all subsequent page navs. That will appear better in a tool like Lighthouse because they're very focused on initial page load, but it's not necessarily better for the user. And it could change a year from now as Google starts measuring soft navs where SvelteKit's default rendering might outperform an MPA.

I very honestly don't know exactly how to measure the trade-offs between SvelteKit's hybrid rendering and MPAs with partial hydration. For now I've been keeping my eye on the soft nav proposal and hoping we can get some better real world data to better demonstrate which one more often performs more strongly in the real world. I did a deep dive into the performance data where Astro touts they're faster than SvelteKit and found this was purely because SvelteKit has more flexible rendering strategies than Astro. With Astro, you can only build an MPA. But SvelteKit lets you chose from different rendering strategies including SPAs which absolutely kill performance (#11637). If you factor out SPAs from the SvelteKit data, then I believe Astro loses all of its performance advantage. I filed #11724 to help us gather this data and better understand the performance trade-offs between rendering modes.

@PierBover
Copy link

PierBover commented Mar 14, 2024

@benmccann it's not only about the KBs downloaded, latency, etc but also the CPU that parses and executes all the uneeded JS. It's objectively and inarguably a waste of resources, regardless of whether Svelte 5 is more efficient or not.

Partial hydration is not only about some minor static elements here and there (footer, nav, etc). There are plenty of use cases where +90% of the DOM is not interactive. Marketing sites, blogs, docs, etc. It's simply a massive waste of resources to have a blog page with say a single interactive form which forces the whole DOM to be downloaded again in JS, parsed, and hydrated. Plus the data, unless Svelte 5 has a way to extract the data needed for hydration from the HTML.

Another point I'd like to make is that there's really no need for a fancy automatic partial hydration system. Astro's solution of manually defining interactive islands is perfectly fine. In fact, probably better so devs have control over what's happening.

@benmccann
Copy link
Member

All of what you say is true for the initial page load, but I'm not sure that any of it is for subsequent page loads. The system you describe wastefully sends, parses, and renders HTML for the header, footer, navigational elements, etc. on soft navs whereas SvelteKit's hybrid rendering fetches only JS for the inner page content and makes an API call returning some JSON that I believe makes page renders after the initial one less expensive. I certainly understand all the points you're making, but I think we need to better understand the tradeoffs. It may be that partial hydration performs better for certain use cases and hybrid rendering better for others. What I'd like to do is gather better data showing where each one performs better than the other and then make sure that we can offer that functionality in a way where we're confident that users will end up with the one that's best for their particular application.

@PierBover
Copy link

The system you describe wastefully sends, parses, and renders HTML for the header, footer, navigational elements, etc.

HTML is vastly more efficient in terms of CPU and memory but ultimately it depends on the use case. Plenty of situations where users rarely navigate more than one or two pages at a time. Again: marketing sites, docs, blogs, etc.

It may be that partial hydration performs better for certain use cases and hybrid rendering better for others.

Certainly. I'm not arguing to remove SvelteKit's current hybrid rendering. On the contrary, I'm arguing in favor of including more options for use cases where IMO Astro is a better fit right now.

And it bears mentioning that Astro is definitely catching up with SvelteKit.

https://npmtrends.com/@sveltejs/kit-vs-astro

@mquandalle
Copy link

mquandalle commented Mar 15, 2024

The page contents are only duplicated on the load of the first page.

For me one of the key features of SvelteKit is the flexibility of rendering modes, as highlighted on the landing page:

SPA? MPA? SSR? SSG? Check. SvelteKit gives you the tools to succeed whatever it is you're building.

I have a website where 95% of users only read one page (coming from Google search results) before leaving. So, in that use case, I would like to optimize for the first page load rather than subsequent rare page navigations.

The pages are completely static with a lot of text and other UI elements except for a small "feedback" module that needs JavaScript. So for these kinds of pages, I think it would make sense to use export csr = false but with the ability to opt-in to "hydration islands" on a per-component basis.

I'm probably underestimating the complexity of this issue, but it seems that the mount/hydrate/unmount API of Svelte 5 would allow for hydrating a single component on a csr = false page.

@itssumitrai
Copy link

One extremely crucial use case for partial hydration is monetization, and specifically like Google ad manager.
With Sveltekit hydration process, if any external script (like Google ad manager) modifies the server side rendered markup, Sveltekit removes any changes during client side rendering as part of hydration.
Basically that forces people to not modify dom until app is mounted, this time is precious and can result in significant amount of $$ loss.
A partial hydration or a way to skip certain parts of page to not use hydration can help alot with monetization

@robinloeffel
Copy link

I would like to take the opportunity and chime in on this. While working on a hobby project of mine, I've noticed that, counter to what I was assuming, setting export const prerender = true; on my root layout.server.ts does not, in fact, turn off client-side routing. I've noticed it, because I have a hero component containing a heavy image at the top of a few pages, and when switching between those pages, I could see the browser working to fetch the new image asset, while the rest of the page had already been switched out.

Alright, no biggie, I thought, checked the docs, and stumbled upon the csr option. So I've appended export const csr = true; right at the end of my layout.server.ts. Tight, all seemed to work great! Until I've tried actually using one of my interactive components. Nothing worked. Back to the docs. Turns out, disabling CSR instructs SvelteKit not to ship any JS, at all! This seems very counter-intuitive to me. Maybe a bit naively, I thought having SSR enabled, but CSR disabled, would load a given page normally, hydrate it, and then handle subsequent navigation steps the vanilla way again. This would've already been enough for me, no need for partial hydration!

Although, having worked with Astro at my day job, and kind of not liking it, because of the whole mixing up two syntaxes thing, but also really liking it because of their Islands Architecture, I, and a lot of other people, would absolutely love it, if SvelteKit supported something similar! I have yet to find another framework that is such a breeze to work with, and having a feature like this would be absolutely baller!

@benmccann benmccann changed the title Partial Hydration via the use:action directive 👀 Partial hydration Aug 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature / enhancement New feature or request size:large significant feature with tricky design questions and multi-day implementation
Projects
None yet
Development

No branches or pull requests