-
-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Svelte 5 next.179+: SSR regression #13115
Comments
Please provide a smaller reproduction. There are a ton of things going on here. As an initial guess without looking at any details, are you running the SSR output through an HTML minifier and/or stripping comments? Server-rendered comments are necessary in order to be able to properly hydrate in the browser. |
Thanks @Conduitry for the quick reply. What I can do is give you the output though. Comments don't appear to be stripped source_ssr.html.txt Inertia expects an empty body with only a <div id=app that includes a data-page attribute for the router information. Based on this it runs Svelte app. |
You can use |
@dummdidumm @Conduitry Here is a minimal reproduction bug
You will see it works just fine. Now upgrade Svelte and it's broken Edit: Updated first post too |
I started exploring this a bit but got stuck and had to leave the desk. It seems that it's trying to hydrate an empty text node but that's the only thing i discovered so far. |
So the culprit here looks to be the wonky stuff that https://github.com/inertiajs/inertia/blob/master/packages/svelte/src/lib/components/Render.svelte In fact, https://github.com/inertiajs/inertia/blob/master/packages/svelte/src/lib/createInertiaApp.ts#L31-L90 Which means that our special comment markers will be missing from the SSR output – meaning that we incorrectly try and match the current hydration DOM node up to the wrong piece of content (as we're expecting comments to be there for blocks, but instead we get actual elements instead). I think there is still an issue with Svelte 5 here too though as at best we should be capturing a hydration mismatch issue here. I wonder if we this sequence of blocks is enough to break the compiler: {#if store.component}
{#key key}
<svelte:component this={component} {...props}>
{#each childComponents as child, index (component && component.length === index ? store.key : null)}
<svelte:self {...child} />
{/each}
</svelte:component>
{/key}
{/if} Update 2: digging more into to above, it seems that the <!-- {#if store.component} -->
{#key key}
<svelte:component this={component} {...props}>
{#each childComponents as child, index (component && component.length === index ? store.key : null)}
<svelte:self {...child} />
{/each}
</svelte:component>
{/key}
<!-- {/if} --> Making that change fixes the issue entirely. The HTML output between the two is also interesting: <!DOCTYPE html>
<html lang="en">
<head>
<script type="module" src="/@vite/client"></script>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Svelte + TS</title>
<script type="module" src="/src/entry-client.ts"></script>
<!--[-->
<!--]-->
<title>Home - SSR Reproduction</title>
</head>
<!--[-->
<div
data-server-rendered="true"
id="app"
data-page='{"component":"Home","props":{"appName":"SSR Reproduction","users":[{"id":1,"name":"Test","email":"test@example.com"}]},"url":"http://localhost:5173/","version":"12345"}'
>
<!--[--><!----><!---->
<nav class="flex items-center space-x-6 bg-slate-800 px-10 py-6 text-white">
<div class="rounded-lg bg-slate-700 px-4 py-1">SSR Reproduction</div>
<a href="/" class="hover:underline">Home</a>
<a href="/users" class="hover:underline">Users</a>
<a href="/article" class="hover:underline">Article</a>
<a href="/form" class="hover:underline">Form</a>
<button type="button" class="hover:underline">Logout</button>
</nav>
<main class="px-10 py-8">
<!----><!--[--><!--[--><!----><!---->
<h1 class="text-3xl">Home</h1>
<p class="mt-6">
<a href="/article#far-down" class="text-blue-700 underline"
>Link to bottom of article page</a
>
</p>
<!----><!----><!--]--><!----><!--]--><!---->
</main>
<!----><!----><!--]--><!----><!---->
</div>
<!--]-->
</html> With <!DOCTYPE html>
<html lang="en">
<head>
<script type="module" src="/@vite/client"></script>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Svelte + TS</title>
<script type="module" src="/src/entry-client.ts"></script>
<!--[-->
<!--]-->
<title>Home - SSR Reproduction</title>
</head>
<!--[-->
<div
data-server-rendered="true"
id="app"
data-page='{"component":"Home","props":{"appName":"SSR Reproduction","users":[{"id":1,"name":"Test","email":"test@example.com"}]},"url":"http://localhost:5173/","version":"12345"}'
>
<!----><!---->
<nav class="flex items-center space-x-6 bg-slate-800 px-10 py-6 text-white">
<div class="rounded-lg bg-slate-700 px-4 py-1">SSR Reproduction</div>
<a href="/" class="hover:underline">Home</a>
<a href="/users" class="hover:underline">Users</a>
<a href="/article" class="hover:underline">Article</a>
<a href="/form" class="hover:underline">Form</a>
<button type="button" class="hover:underline">Logout</button>
</nav>
<main class="px-10 py-8">
<!----><!--[--><!----><!---->
<h1 class="text-3xl">Home</h1>
<p class="mt-6">
<a href="/article#far-down" class="text-blue-700 underline"
>Link to bottom of article page</a
>
</p>
<!----><!----><!----><!--]--><!---->
</main>
<!----><!----><!----><!---->
</div>
<!--]-->
</html> |
Thanks for the quick investigation @trueadm :) My reproduction exemple use my own fork of Inertia because I have converted the components to be Svelte 5 so the proper source would be https://github.com/jamesst20/inertia/blob/main/packages/svelte/src/lib/components/Render.svelte What should be done instead? I could investigate further why the component could be null, but the createInertiaApp(...) signature I believe expects null components to be valid I guess if only props update are expected and the router shouldn't act. |
@jamesst20 Actually, I noticed that the above commenting out of the if block still hits a hydration mismatch, except you are not passing So I think the server.js replacement should be: const html = template
.replace(`<!--app-head-->`, rendered.head?.[0] ?? "")
.replace('<!--app-body-->', rendered.body ?? ""); Then in your <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Svelte + TS</title>
<script type="module" src="/src/entry-client.ts"></script>
<!--app-head-->
</head>
<body>
<div id="app" data-page='{{ page }}'><!--app-body--></div>
</body>
</html> However, that doesn't seem to work and I can't tell why as the whole thing is wonky. It also seems like we also render another element with the It's so hard to debug what's going on because of the odd way this project is setup. However, the |
I'm really sorry about the reproduction exemple. It's very possible it is a little off as it is not a real world usage at all, I was required to provide a smaller reproduction exemple. A real world demo that is supposed to be accurate is https://github.com/jamesst20/inertia/tree/main/playgrounds/svelte if you check first post edits, I had given instructions on how to test it I believe one of your question the answer lies here: https://github.com/jamesst20/inertia/blob/main/packages/svelte/src/lib/components/SSR.svelte#L13 and here https://github.com/jamesst20/inertia/blob/main/packages/svelte/src/lib/createInertiaApp.ts#L49 I don't know why Inertia works this way but the way it is meant to work is
|
So looking into this, and it appears the issue is you have a |
This feels like a different bug but a bug nonetheless...the custom element is not from svelte so this seems something we should probably address right if we want custom elements to work in general |
I think a new issue around this would be good then, as Svelte 5 is working as intended here. If someone mutates the DOM outside of Svelte's knowledge, then things are obviously going to break because we now create templates from their code. If they put a |
I was unable to get it working – sorry. Would it be possible for you to try without Inertia and using something like SvelteKit? |
Not sure if this made that clear, but if I were to do const html = template
.replace(`<!--app-head-->`, rendered.head?.[0] ?? "")
.replace('<!--app-body-->', rendered.body ?? ""); <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Svelte + TS</title>
<script type="module" src="/src/entry-client.ts"></script>
<!--app-head-->
</head>
<body>
<div id="app" data-page='{{ page }}'><!--app-body--></div>
</body>
</html> because the SSR renders the Svelte SSR component which already includes the <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Svelte + TS</title>
<script type="module" src="/src/entry-client.ts"></script>
<!--app-head-->
</head>
<body>
<div id="app" data-page='...'>
<div id="app" data-server-rendered="true" data-page='...'>
some html rendered content
</div>
</div>
</body>
</html> this is why I am doing this in the reproduction exemple, I replace the whole body content const rendered = await render(
await res.inertiaObject(component, { appName: "SSR Reproduction", users })
);
const templateBody = parse(template).querySelector("body").toString();
const html = template
.replace(`<!--app-head-->`, rendered.head?.[0] ?? "")
.replace(templateBody, rendered.body ?? ""); |
Shall I create a new issue? |
Yes please 🙏🏻 |
Please.
So you can't do that. The content that Svelte renders, the You should never have Svelte to render the HTML for the element that you intend to use as your entry point into your app. |
Done |
I have removed Inertia as a dependency and written simplified code to mimic only necessary part There is a boolean in server.js to toggle ON/OFF SSR but you can also use ssr=false/true as a get parameter. I created a different html file for ssr and for normal rendering. About your explanation, I am not really sure if it is something I don't see or understand or something you might have not seen about inertia. I hope the example clarifies it. Edit: Ok I think I get your explanation I am investigating further now |
You were right about the markers. Because the I wrote a fix here to ensure only the content of the div is rendered by Svelte jamesst20/inertia@c8493ab It now renders properly, but I still get an hydratation error missmatch
By looking at the before/after, I can see there are markers at the end in the SSR response but not after hydratation. Do you have any idea? Breakpoint is before hydratation is called |
Thats good, I can look into that tomorrow. Is the repro updated to reflect your changes? |
Have you had the chance to take a look? Should I create a new issue and close this one as I believe it might be unrelated? |
@jamesst20 I'm still digging into it. I'll let you know when I find out more :) |
I get a ton of errors when trying to run
|
This is my bad, after step 6 you need to run if it says command not found you can also grab it here https://getcomposer.org/download/2.7.9/composer.phar and run instead |
@jamesst20 Thanks I got it working. When I remove the content from |
So it seems like disabling svelte({
compilerOptions: {
hmr: false
}
}), Not sure why |
With HMR turned on, in some places extra comments are added on the client so HMR can replace the components |
Okay, so after digging into this for hours with @dummdidumm it turns out the issue is with |
What should be done? I must admit I do not use Laravel in my own projects, I use Inertia with Ruby on Rails but I did my testing with the closest playground I had which was the Laravel one. Should we open an issue here? https://github.com/laravel/vite-plugin I wouldn't know what to say though |
I honestly don't know enough about |
Maybe it's this line that's setting hmr to cc @dominikg |
I have created an issue at the plugin repository just in case they can provide some insights about this issue as it doesn't appear Svelte is at fault? laravel/vite-plugin#304 |
vps sets hmr here
edit: vps does not set viteConfig.server.hmr, it just reads it. Maybe try running the setup with |
Closing this as I feel like there's a solution linked from laravel/vite-plugin#304. |
Edit: The described bug has been resolved, but there is a new one. It might be faster to simply skip to this comment: #13115 (comment)
Describe the bug
For some reasons, starting with Svelte 5 build 179 up to the last I have tested (next.241) I am getting this error when using SSR:
I have tried to pin point what in the DOM could be causing this but I was unsuccessful. Sometimes only wrapping something in a div trigger the error.
Reproduction
ssr-reproduction-bug.zip
Svelte 5 build 178 works perfectly.
Svelte 5 build 179 we get a first render then the error
Svelte 5 build 180+ blank page identical error
Logs
No response
System Info
Severity
blocking an upgrade
The text was updated successfully, but these errors were encountered: