Skip to content

SSR: Styles of child components are missing when css: injected and bind:prop are used #15543

@molily

Description

@molily

Describe the bug

We've migrated a sizeable code base to the Svelte 5 compiler, while some components still use the legacy Svelte 4 syntax.

We've found a regression regarding the server side rendering of Svelte 4 components when css: 'injected' is used.

Reproduction

Please see:
https://github.com/molily/svelte-server-test

Two components:

SvelteTestComp.svelte:

<script>
  import Child from './Child.svelte';

  let hello = 'hello';
</script>

<p>SvelteTestComp.svelte</p>

<Child bind:hello />

<style>
  * {
    border: 2px solid red;
    padding: 1em;
  }
</style>

Note the bind:hello!

Child.svelte:

<script>
  /** @type {string} */
  export let hello;
</script>

<p>Child.svelte hello: {hello}</p>

<style>
  * {
    border: 2px solid blue;
    padding: 1em;
  }
</style>

SSR code in node build-server.js:

import { render } from 'svelte/server';
import SvelteTestComp from './SvelteTestComp.svelte';

const { head, body } = render(SvelteTestComp, { props: {} });

console.log('body', body);
console.log('head', head);

build-server.js creates a SSR build with esbuild-svelte.

node build-server.js

The output is written to dist/, it's already in the repo:

https://github.com/molily/svelte-server-test/blob/main/dist/svelte-render-server.js

This output looks perfectly fine to me. The styles of both components are in the code:

var $$css = {
  hash: "svelte-t13rfg",
  code: ".svelte-t13rfg {border:2px solid blue;padding:1em;}"
};
function Child($$payload, $$props) {
  $$payload.css.add($$css);
// SvelteTestComp.svelte
var $$css2 = {
  hash: "svelte-1d6hxxn",
  code: ".svelte-1d6hxxn {border:2px solid red;padding:1em;}"
};
function SvelteTestComp($$payload) {
  $$payload.css.add($$css2);

So I assume the problem is not in esbuild-svelte.

Now, render SvelteTestComp using the bundle:

node dist/svelte-render-server.js

Output:

body <!--[--><p class="svelte-1d6hxxn">SvelteTestComp.svelte</p> <p class="svelte-t13rfg">Child.svelte hello: hello</p><!----><!--]-->
head <style id="svelte-1d6hxxn">.svelte-1d6hxxn {border:2px solid red;padding:1em;}</style>

Note the missing <style> for Child (with the blue border).

What's causing this problem? We've managed to trace it back to bind:hello. 🤯

Either remove it or change it to a non-binding prop passing {hello}, make a build, run the build.

Output without bind:

body <!--[--><p class="svelte-1d6hxxn">SvelteTestComp.svelte</p> <p class="svelte-t13rfg">Child.svelte hello: hello</p><!----><!--]-->
head <style id="svelte-1d6hxxn">.svelte-1d6hxxn {border:2px solid red;padding:1em;}</style><style id="svelte-t13rfg">.svelte-t13rfg {border:2px solid blue;padding:1em;}</style>

This is what I'm expecting in the bind:hello case as well. Am I missing anything?

Thanks a lot for looking into this. Much appreciated.

System Info

Binaries:
    Node: 23.9.0 - ~/.n/bin/node
    npm: 11.2.0 - ~/.n/bin/npm
  npmPackages:
    svelte: 5.23.2
    esbuild: 0.25.1
    esbuild-svelte: 0.9.2

Severity

annoyance

Metadata

Metadata

Assignees

No one assigned

    Labels

    cssStuff related to Svelte's built-in CSS handling

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions