Skip to content

SSR not working with Svelte 5 - render() returns empty object #192

@timmywil

Description

@timmywil

Server-side rendering fails with live_svelte v0.16.0 and Svelte 5 because the render() function returns an object with getters that don't serialize properly.

Environment

  • live_svelte: v0.16.0
  • svelte: v5.41.2
  • esbuild-svelte: v0.9.3
  • Node.js: v22.20.0

Expected Behavior

Svelte components should render server-side, with the HTML content visible in the page source.

Actual Behavior

Components don't render server-side. The LiveView receives an empty object %{} from NodeJS instead of the rendered HTML.

Root Cause

Svelte 5's render() function from svelte/server returns an object with getters for html, head, and body properties:

Object.defineProperties(result, {
  html: { get: () => { ... } },
  head: { get: () => { ... } },
  body: { get: () => { ... } }
})

When NodeJS.call! serializes this object to send back to Elixir, it only sees {} because property getters aren't enumerable by default.

This appears to be a breaking change in how Svelte 5's render() function returns data compared to Svelte 4. The migration guide in live_svelte's README mentions Svelte 5 support but doesn't mention this issue.

Workaround

Modify assets/js/server.js to explicitly access the getters:

import * as Components from '../svelte/**/*.svelte'
import { getRender } from 'live_svelte'

const renderFn = getRender(Components)

// Wrap the render function to extract the getter values
// Svelte 5's render() returns an object with getters for html/head/body
// which nodejs library doesn't serialize, so we need to access them explicitly
export function render(name, props, slots) {
  const result = renderFn(name, props, slots)
  return {
    html: result.html,
    head: result.head,
    body: result.body
  }
}

Proposed Fix

The getRender() function in live_svelte's source should be updated to handle Svelte 5's getter-based return values. This could be done in the render function provided by live_svelte, similar to the workaround above.

Steps to Reproduce

  1. Install live_svelte v0.16.0 with Svelte 5
  2. Create a simple Svelte component in assets/svelte/test.svelte:
    Test
  3. Render it in a LiveView with SSR enabled:
    <.svelte name="test" props={%{}} socket={@socket} />
  4. Check page source - the component won't be server-rendered

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions