svelte icon indicating copy to clipboard operation
svelte copied to clipboard

Empty comment in HTML breaks hydration

Open Azarattum opened this issue 1 year ago • 5 comments

Describe the bug

The Problem

I'm trying to implement a manual hydration for some parts of my application. Which means I need to render components to a string and pass them to @html to avoid Svelte hydrating this parts of my application automatically. E.g.

<script>
  import { render } from "svelte/server";
</script>
 
<!-- svelte-ignore hydration_html_changed -->
{@html import.meta.env.SSR ? render(someComponent).body : " "}

The problem is that render(someComponent).body contains empty comments (<!---->) for its future hydration. And you get a mismatch because these comments are misinterpreted as the end of @html block.

Possible Solution

Make sure that @html block are closed with a "hash comment" and ignore every other comment. This change should be as simple as replacing (next).data !== '' with (next).data !== hash here. If this is an acceptable solution, I can make a PR.

Use Case

My use case might seem weird to you. I've posted more details on discord. We could discuss it there if needed.

Reproduction

Minimal reproduction:

{@html "<!---->"}

Observe:

hydration_mismatch:
  Hydration failed because the initial UI does not match what was rendered on the server

Logs

No response

System Info

System:
    OS: macOS 14.6.1
    CPU: (8) arm64 Apple M1
    Memory: 135.31 MB / 16.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 23.1.0 - /opt/homebrew/bin/node
    npm: 10.9.0 - /opt/homebrew/bin/npm
    pnpm: 9.4.0 - ~/Library/pnpm/pnpm
    bun: 1.1.31 - ~/.bun/bin/bun
  Browsers:
    Brave Browser: 130.1.71.123
    Safari: 17.6
  npmPackages:
    svelte: ^5.0.0 => 5.0.2

Severity

annoyance

Azarattum avatar Nov 16 '24 11:11 Azarattum

The hash is only added during dev to give you the runtime error in case of mismatch and will not be there in production.

But also, considering that what you are trying to achieve is purely client side why do you need this?

paoloricciuti avatar Nov 16 '24 13:11 paoloricciuti

Hmm... So, there are no hashes in prod... Could it be a good idea to make comments defining @html block different from the others anyway (even without hashes)?

While most of what I need is client-side, it still operates on DOM coming from the server (CSR-only is unacceptable in my use case). Since Svelte doesn't have partial (component level) hydration yet, using the @html and render() is the only way I've found to opt out of hydration for a piece of code. Here is a small demo of what I have achieved so far: REPL.

The ultimate goal of this is to preserve state (including DOM references and any modifications done outside of Svelte) between route navigations. E.g. imagine a tab-view where each tab is its own route with SSR, but when navigating between them on a client you keep all the state for each tab. Unfortunately, snapshot API or using layouts is too limiting. This is my I'm making this.

Azarattum avatar Nov 16 '24 14:11 Azarattum

The importance of keep-alive like behaviour has already been discussed here. This solution would also be a possible workaround for https://github.com/sveltejs/svelte/issues/12895 - REPL, since it uses global IDs for component caching.

Azarattum avatar Nov 16 '24 14:11 Azarattum

I've made a full-stack tab-view demo. Note the content!.body.replaceAll("<!---->", "<!----->") in KeepAlive.svelte. The replace is a workaround for the problem described in this issue. I'm not sure how safe it is to do that. Is it OK for hydration to accept any comment or should it always have data = ""? From my testing this workaround kind of works so far. I'm not sure, though. Maybe I'm missing something.

Azarattum avatar Nov 17 '24 03:11 Azarattum

I wonder if the correct solution is that we have a better API for these cases, see https://github.com/sveltejs/svelte/issues/14337.

trueadm avatar Nov 17 '24 20:11 trueadm

https://github.com/sveltejs/svelte/issues/14337 would solve this issue.

Unfortunately, this issue is just a part of a problem for my use-case. There are some other problems with my current implementation. For example, any transitions inside KeepAlive wound't work (since components aren't technically mounted). Ideally such the functionality should be provided on a framework level instead of being hacked on top in userland. https://github.com/sveltejs/svelte/issues/6040 was closed waiting for an RFC. I guess I can draft one if you think this is the right direction. Otherwise if we want to keep this in userland, we need more advanced APIs for these use cases (e.g. see https://github.com/sveltejs/svelte/issues/6942#issuecomment-2481085214). I think @Rich-Harris mentioned that he has ideas for a new transition API. I'm curious if they could align with our goals here.

Azarattum avatar Nov 18 '24 02:11 Azarattum

I wonder if the correct solution is that we have a better API for these cases, see #14337.

I don't think #14337 solves this use case because of the intention to hydrate the ssr-d output (to avoid svelte-controlled unmount). #14337 would help with purely server components with no interactivity or just static email markup.

This issue is purely on the hydration mismatch warning (supposedly in dev only) when used in rendering components via the server-side render() api function, placing the output in {@html } and then calling hydrate() on the component load on the client-side.

leonidaz avatar Nov 18 '24 22:11 leonidaz

After speaking to @Rich-Harris about this, we feel the correct fix for this is to make it so the @html block can handle comments inside the content without hydration breaking. This would involve adding starting and ending comments to the content for the raw block.

trueadm avatar Nov 19 '24 15:11 trueadm