Skip to content

Conversation

@Rich-Harris
Copy link
Member

@Rich-Harris Rich-Harris commented Oct 26, 2025

Extremely WIP. The tests pass, but it's an illusion — there's much still to do.

This will enable out-of-order rendering, which is to say that in a situation like this, where something is awaited in the <script>...

<!-- +layout.svelte -->
<script>
  import { getUser } from '$lib/data.remote';

  let { children } = $props();

  const user = $derived(await getUser());
</script>

<nav>
  {#if user}
    <p>hello {user.name}!</p>
  {:else}
    <a href="/login">log in</a>
  {/if}
</nav>

<main>
  {@render children()}
</main>

...the component's contents can begin rendering immediately — async work in {@render children()} needn't wait for the unrelated async work in the layout.

The basic strategy is this: any statements or declarations containing await — or any statements or declarations that follow them — are wrapped in async functions that run sequentially. So this...

var a = 1;
await sleep();
var b = 2;
await sleep();
var c = 3;

...becomes this:

var a = 1;
var b, c;

var $$promises = $.run([
  () => sleep(),
  () => b = 2,
  () => sleep(),
  () => c = 3
]);

Any parts of the template that depend on b must wait for $$promises[1] to resolve, while any that depend on c must wait for $$promises[3].

Things still to do:

  • equivalent of $.run in SSR (right now it's a noop)
  • fix some hydration bugs that don't show up in tests but are definitely there
  • handle corner cases, like x = $state() followed by x = {...} — anything that depends on x must wait for the latter promise to resolve. This means analysing statements for mutations or function calls that could cause mutations; it may be the case that in some cases we need to deopt and await the final promise
  • tidy up the code

Helps with #16561
Also fixes #16978
Solves #17049 (comment) (but possibly not everything in that issue)

Follow-up work:

  • reorder statements? So far I didn't do this, because it's potentially dangerous. But it would be nice if we could — for example — yoink $derived expressions as high as they'll go and run them in parallel

Before submitting the PR, please make sure you do the following

  • It's really useful if your PR references an issue where it is discussed ahead of time. In many cases, features are absent for a reason. For large changes, please create an RFC: https://github.com/sveltejs/rfcs
  • Prefix your PR title with feat:, fix:, chore:, or docs:.
  • This message body should clearly illustrate what problems it solves.
  • Ideally, include a test that fails without this PR but passes with it.
  • If this PR changes code within packages/svelte/src, add a changeset (npx changeset).

Tests and linting

  • Run the tests with pnpm test and lint the project with pnpm lint

Copy link
Member

@dummdidumm dummdidumm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great! Only a few nits and questions, but overall it seems good.

@Rich-Harris Rich-Harris merged commit 1126ef3 into main Oct 28, 2025
18 checks passed
@Rich-Harris Rich-Harris deleted the out-of-order-rendering branch October 28, 2025 16:50
@github-actions github-actions bot mentioned this pull request Oct 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Svelte boundaries fail with remote function and being called in script

3 participants