Skip to content

feat: out of order rendering#17038

Merged
Rich-Harris merged 93 commits intomainfrom
out-of-order-rendering
Oct 28, 2025
Merged

feat: out of order rendering#17038
Rich-Harris merged 93 commits intomainfrom
out-of-order-rendering

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
dummdidumm added a commit that referenced this pull request Nov 16, 2025
This fixes #17075 by solving the TODO of #17038 to add out of order rendering for async `@const` declarations in the template.

It's implemented by a new field on the component state which is set as soon as we come across an async const. All async const declarations and those after it will be added to that field, and the existing blockers mechanism is then used to line up the async work correctly. After processing a fragment a `run` command is created from the collected consts.
Rich-Harris added a commit that referenced this pull request Nov 17, 2025
* fix: parallelize async `@const`s in the template

This fixes #17075 by solving the TODO of #17038 to add out of order rendering for async `@const` declarations in the template.

It's implemented by a new field on the component state which is set as soon as we come across an async const. All async const declarations and those after it will be added to that field, and the existing blockers mechanism is then used to line up the async work correctly. After processing a fragment a `run` command is created from the collected consts.

* fix

* tweak

---------

Co-authored-by: Rich Harris <rich.harris@vercel.com>
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

2 participants