These posts are aggregated from Svelte GitHub Repository.
Hi! To be honest, I am not a big fan of Svelte 5 Runes and did enjoy the simplicity of $:
reactivity in Svelte3/4.
So I am working with Svelte on a daily basis and we have a code base mainly using 3/4 syntax. Now the first components use Svelte 5 Runes but have to work with former Svelte 4 components (composition).
So it came as it had to come and over a bind:value attribute I've got a Proxy(Array) instead of an Array. My former code checked for an Array using Array.isArray
, but this obviously fails. What can I do about it?
I have the following Svelte code and since I added Tailwindcss, the server does not reload properly. To be more precise, updates to CSS files do not appear at all, even when I manually refresh the page. The only exception is if I also update an HTML file. In that case, the changes to HTML are shown and if I refresh the page manually, the CSS is also updated. To summarize:
To use tailwind, I followed the instructions. I don't know if there is something I did wrong. Maybe tailwind does not automatically refresh the "static" directory or what?
I've seen this topic mentioned in https://github.com/sveltejs/svelte/discussions/15822, but I feel it could be better discussed as a potential feature rather than a question.
Currently, if I need to pass a snippet as a parameter to a @render
tag, I need to declare it beforehand, like so:
{#snippet dataItem(name: string, value: Snippet)}
<div>
<dt>{dtValue}:</dt>
<dd>{@render ddValue()}</dd>
</div>
{/snippet}
{#snippet value()}<strong>ipsum</strong>{/snippet}
{@render dataItem("Lorem", value)}
On top of creating many single-use variables (if rendered more than once), which can lead to mistakes, it's also just a very labored way of doing it. It's problematic to the point where it almost makes more sense to copy-paste the contents of the snippet to avoid those issues.
One way to fix the problem would be to allow for @render
to receive children, like so:
{#render dataItem("Lorem", value)}
{#snippet value()}<strong>ipsum</strong>{/snippet}
{/render}
This approach would have a few problems:
@render
tag would now need to be written as #render
, though it could simply support both ways;Another way would be to pass anonymous snippets directly in the @render
call, like so:
{@render dataItem(
"Lorem",
{#snippet}<strong>ipsum</strong>{/snippet}
)}
This would circumvent the first two issues above, but it would mean blending HTML into what I think is supposed to only accept JS, which seems to be something Svelte managed to avoid doing up until now.
I had originally planned to post this as a feature request, but since I'm undecided as to which feature should be introduced, if any at all, I think I'll defer to the more experienced Svelte devs as to how to solve this problem.
Hi all,
While building our app with Svelte, we created an AI agent indexed on the latest repositories of both Svelte and Svelte Kit.
It is available here: https://app.commanddash.io/agent/svelte (no setup) and also in VSCode.
(maintainers if reading this) the agent updates regularly and gives up-to-date answers. would love if we can highlight this for others to leverage
I’m wondering about best practices for using it across a Svelte app.
Is it recommended to:
This is a long document; get yourself a cup of tea.
You can now use the await
keyword in Svelte — in your <script>
, inside $derived
expressions, and in your markup — by installing the async
branch...
npm i https://pkg.pr.new/svelte@async
...and adding the experimental.async
option to your svelte.config.js
(or wherever you configure Svelte):
// svelte.config.js
export default {
compilerOptions: {
experimental: {
async: true
}
},
kit: {
// ...
}
};
You can also try things out in the async playground.
You will find bugs! This is in no way production-ready. The PR that accompanies this discussion is at https://github.com/sveltejs/svelte/pull/15844.
In olden times, we did asynchronous work like fetching data inside onMount
or an {#await ...}
block. This works but it's quite verbose, and offers no coordination — if two components are both fetching stuff, then they will likely have independently managed error/loading states, often resulting in a janky UI with multiple spinners.
Frameworks like SvelteKit (or Remix or pre-App-Router Next.js, etc) offer an alternative: instead of fetching inside the component, we do our asynchronous work outside the component, for example in load
functions. This typically results in better user experience (we can server-render the data, preload it, and coordinate everything) but it too has problems — prop-drilling, type shenanigans, coarse-grained invalidation, and logic that's hard to delete because it's often not obvious whether something in your load
function is even being used.
In recent years component frameworks have explored a third way: putting asynchronous work back inside components, but in a way that is coordinated:
<Suspense>
, startTransition
, useTransition
, use
and React Server Components, that together allow you to manage async updatescreateResource
API that can be used with <Suspense>
and startTransition
/useTransition
for the same purposeawait
inside a <script setup>
in a component inside a <Suspense>
boundary, though this experimental API only works for component creation, not subsequent updatesWe believe Svelte's compiler-centric nature offers us a way to have component-level async work with significantly better ergonomics and fewer drawbacks than existing approaches.
await
keyword. Avoid framework-specific APIs and idioms to the extent possible<script>
, inside $derived
expressions, in the template, in attributes and component props, in control flow like {#if ...}
blocks, or wherever else)await
expressions need not result in sequential async work. Similarly, asynchronous work in two sibling components (for example) should happen simultaneouslyfoo
changes, and await bar(foo)
is (or will be) visible on the page, any occurrences of foo
in the UI should not update until bar(foo)
resolvesWe also want this to have minimal impact on performance and memory for existing codebases, and to cause little or no breakage for existing apps.
In a nutshell: you can now use await
in three places that will today result in a syntax error:
<script>
$derived
expressionWe also introduce a pending
snippet to <svelte:boundary>
, which allows you to provide UI when await
expressions inside the boundary are first resolving. For now, await
expressions must be inside a boundary with a pending snippet (however deeply nested — for example you might have a single boundary at the very root of your app), though this constraint will likely be relaxed in future.
If state is read inside a $derived
or template expression with an await
, changes to that state will not be reflected in the UI until the expression resolves. For example in a situation like this...
<h1>Weather forecast for {city}</h1>
<p>{await getWeatherForecast(city)}</p>
...the <h1>
will not update until the <p>
does. (If getWeatherForecast
were to fail, it would activate the nearest error boundary.)
To know if an update is currently pending, you can use $effect.pending()
:
{#if $effect.pending()}
<p>loading new data...</p>
{/if}
If unrelated state changes while an update is pending, it will be visible immediately (assuming it doesn't separately cause asynchronous updates).
The most obvious use case is loading data (which could be as simple as fetch
, but in many cases will likely involve yet-to-be-designed utilities for client-server communication), but others include:
<img alt="..." src={await preload('...')}>
)As much as framework authors like to witter on about rendering performance, the thing that really slows apps down is latency. In some cases you can reduce latency by speeding up your back end, or prefetching content you expect to need, or serving content from somewhere close to the user, but those things aren't always possible. What you can do is mitigate the effect of latency by minimising the number of round trips between the browser and the server.
Svelte helps by doing as much work as possible in parallel. For example here...
<li>apples {await getPrice('apple')} each</li>
<li>bananas {await getPrice('banana')} each</li>
<li>canteloupes {await getPrice('canteloupe')} each</li>
...the three calls to getPrice
will happen simultaneously, even though they appear in sequence. Similarly, the three wise monkeys will do all their async work together:
<WiseMonkey verb="see" />
<WiseMonkey verb="hear" />
<WiseMonkey verb="speak" />
Not all work can be parallelized. Any await
expressions inside the <script>
run before expressions in the template, and in the sequence you would expect...
// `a` will be calculated and _then_ `b` will be calculated
let a = $derived(await foo(x));
let b = $derived(await bar(x));
...though note that if x
subsequently updates, a
and b
will be recomputed in parallel. You can of course avoid the initial waterfall like so:
let aPromise = $derived(foo(x));
let bPromise = $derived(bar(x));
let a = $derived(await aPromise);
let b = $derived(await bPromise);
While unnecessary waterfalls can generally be prevented by pushing async work as far into the 'leaves' of your application as possible, there are also necessary waterfalls to contend with. For example, you can't use Promise.all
here, because you need to know artist.id
before you can call getTrackListing
:
let artist = $derived(await search(query));
let tracks = $derived(await getTrackListing(artist.id));
It's better if you can do both fetches on the server, near your database. There are two ways to solve this: either anticipate the need for the track listing when you do the search...
let [artist, tracks] = $derived(await searchAndGetTrackListing(query));
...or use a mechanism like React Server Components, where the rendering happens on the server. Option 1 has less-than-ideal ergonomics, though it's basically equivalent to SvelteKit's load
function. Option 2 is optimal from a waterfall prevention perspective, but we feel RSCs have significant trade-offs.
We anticipate that opinionated data-fetching patterns will emerge over time, along with new approaches inspired by RSCs, to solve this problem.
The work presented so far is the first step of a multi-stage process. While it's useful in its current form, more value will be unlocked with later stages:
Today, server-side rendering is fully synchronous. Because of this, if a <svelte:boundary>
with a pending
snippet is encountered during SSR, the pending
snippet will be rendered.
It would be better to render the content instead, but this requires that SSR become an asynchronous operation.
In an ideal world we would also be able to stream the result where appropriate. This is complicated by the fact that you ideally need to know what's in the <head>
before you get to the <body>
. We have some ideas for how to design this sensibly, but it's not a current priority.
Once we get to this part, it's likely that the requirement for await
expressions to be contained in a <svelte:boundary>
will go away.
In SvelteKit we preload the code and data necessary for a navigation before it even occurs: when you hover over a link, or tap it, we import any modules needed by the route and begin running whichever load
functions need to run. (This behaviour is configurable, of course.)
To make this work in a world where asynchronous work happens inside components, we need to be able to pretend that a state change occurred, and do the resulting updates 'off-screen'. The result can either be applied or discarded.
Colloquially, we've been describing this as a 'fork'. You could imagine several unresolved forks coexisting simultaneously until one reality is chosen (by the user clicking on one of several links they've been close to interacting with, for example). Most likely, we'll add an API to Svelte that allows frameworks like SvelteKit (but also particularly ambitious application authors) to enter the multiverse.
This all sounds very complicated, but in reality it's not all that different to how asynchronous updates already work. Nevertheless, it will take some finagling to get right, and in the interests of shipping we haven't included this work in the current PR.
Unless we want to create server endpoints for everything we want to fetch
inside a component, we'll need tools for interacting with the server. Those tools will need to consider security, HTTP caching, mutations and invalidation, optimistic UI, batching, streaming, custom serialization, type safety and lots of other things.
We're at the early stages of figuring out what this all looks like, but we're very confident that the foundation we're laying here will allow us to design something really good.
Anticipating everyone's reaction: no, we're not going to suddenly make you rewrite all your SvelteKit apps. But taking all the other stuff together, a picture starts to emerge: a version of SvelteKit that's a thinner layer on top of Svelte. For example, as proud as we are of our zero-effort type safety, it's something you no longer need if you're fetching data directly inside your component.
Meanwhile the framework internals could potentially get simpler, because we'd be able to rely on common primitives inside Svelte itself. (Today, we can't reuse that much code between the server and client portions of SvelteKit, and we rely a lot on virtual modules and generated code. Some of that is for historical reasons, but some is necessary because anything asynchronous has to happen outside Svelte.)
It seems likely that a lot of interesting new possibilities will open up as a result of this work, and it seems at least plausible that we'll collectively find ourselves drifting away from primitives like load
. Exactly what this would look like is to be determined, and it's something that we'll figure out together as a community.
startTransition
/useTransition
If you've used React or Solid's suspense implementation, you will have encountered startTransition
and useTransition
. These functions allows you to update state in such a way that the nearest suspense boundary doesn't show its fallback while waiting for any async work that is downstream of the state change.
In Svelte, we don't do this. Instead, state changes that result in asynchronous work always have their effects deferred until the asynchronous work is complete. While that work is ongoing, $effect.pending()
is true
. Any pending
snippets are only shown when a boundary is first being created.
This does create the possibility that something distantly connected to a given piece of state has the power to delay (or prevent!) changes to that state from being reflected in the UI. We think this is preferable to the alternative (in which updates outside a useTransition
or similar cause unwanted fallback UI to appear), though it may be necessary to develop techniques for identifying these chains.
In a case like this...
<p>{a} + {b} = {await fetch(`/add/${a}/${b}`).then((r) => r.json())}</p>
...it's possible to imagine a scenario in which a change to a
is followed by a change to b
while the first fetch
is ongoing. If the second fetch
somehow finishes first, we don't want the first fetch
to be applied afterwards, since the resulting UI would show stale values.
As such, a given async expression must only resolve after its previous versions have also resolved.
Normally in Svelte, $derived
expressions use 'push-pull' reactivity — they are invalidated when their dependencies change (the 'push') but are not re-evaluated until something reads them (the 'pull'). This doesn't work for async deriveds, because if we waited until the value was read the resulting promise would never resolve in time.
Instead, an 'async derived' is really just an effect and a source signal in a trenchcoat. We evalute the expression in the effect, and when the promise resolves, set the source value (unless the effect fired again in the interim).
For the most part, you don't need to think about this. There is one important implication though — while it's possible to create an 'unowned derived' (in other words, a derived that is not part of the 'effect tree', such as one created in an event handler) it is not possible to create an unowned async derived, because effects can only be created inside other effects. (Don't worry if you didn't follow this — it's inside baseball stuff, and describes an edge case you're unlikely to encounter. Svelte will tell you if you get this wrong.)
When a reaction (i.e. an effect or derived) runs, we track its dependencies by seeing which values are read during its execution. In pseudo-code:
let dependencies = null;
function get(source) {
dependencies?.add(source);
return source.value;
}
function update_reaction(reaction) {
dependencies = new Set();
reaction.fn();
reaction.dependencies = dependencies;
dependencies = null;
}
(This is simplified and wrong, but you get the idea.)
For asynchronous functions, this presents a challenge. To understand why, consider the order in which things happen here:
function a() {
console.log('a 1');
b();
console.log('a 2');
}
async function b() {
console.log('b 1');
await 0;
console.log('b 2');
}
a();
This will log a 1
, b 1
, a 2
, b 2
— in other words despite the fact that b
isn't even awaiting an asynchronous value, b 2
isn't logged until after it has returned to a
.
In Svelte terms, this would mean that $derived(await a + b)
or an equivalent {await a + b}
template expression would register a dependency on a
but not on b
. But since we're a compiler we have a trick up our sleeves: we wrap the await
expression in a function that restores the effect context, so that b
is treated as a dependency.
On one level this is spooky compiler magic. But on another, it's just making the system work how a reasonable person would expect it to work. It does mean that if you extract the logic out into a function — {await a_plus_b()}
— it will be treated differently, but during development Svelte will catch those cases and help you fix them.
$derived.by
While you can do this...
let x = $derived.by(async () => await y);
...x
will then be a Promise
, not the value of await y
. There's no sensible place to put the await
that would result in x
not being a Promise
. (This is why it's pretty great that we're a compiler and can make the expression form the default, even though it makes other framework authors mad.)
If you have sufficiently complex logic that you need to use a function block, just declare the function and call it:
async function add(p1: Promise<number>, p2: Promise<number>) {
return await p1 + await p2;
}
let sum = $derived(await add(p1, p2));
Note the aforementioned caveat around context preservation — make sure you pass state into the function, rather than having the function close over the state it references, so that dependencies are correctly attached.
Normally, state does not update in the UI until everything that depends on it has finished resolving. This does not apply to a focused <input>
element, which in many cases will be the source of the state change in question — instead, the rest of the UI 'catches up' to the input.
Because of how async reactivity works, there is one small and unavoidable breaking change: beforeUpdate
and $effect.pre
callbacks no longer run before control flow blocks are updated. If you're using those callbacks to set state that control flow logic depends on — which you absolutely shouldn't be — then you may experience breakage. As a result, we may have to wait until 6.0 for a non-experimental release of this stuff.
That was a lot; congratulations to those of you still here. I am sure you have questions, and that's a good thing. This is the place to ask them, or to offer feedback on the design. Let's go!
Hello there!
Let me start but saying that by no means this is a rant. I have a great respect for the core team and I love working with svelte. I've also got my first contribution approved and was super happy to finally contribute back to the framework!
Recently (like maybe a couple month) it feels like stability is somewhat declining. Some issues are getting fixed and some new ones appear. There are currently 6 issues with p0 label some of which are dating back to the end of April.
We are using svelte heavily in production and currently are in a bit of a weird spot. We can't really downgrade because we are using some newer features like attachments and need some newer bugfixes, but we also can't really upgrade to get some of the issues fixed because something new gets broken. Also it gets increasingly difficult to hunt for reproductions. It was never easy, at some point I've spent a week to find what is causing a memory leak, but now it feels like it's even harder.
We've started using svelte about a year ago and back then it was sort of the opposite. It felt like every new release was providing greater stability and performance.
Maybe the core team is also noticing this? Are there any opinions on the matter?
Back in the days it was also extremely helpful when we had some calls with Dominic and he was helping me figure out reproductions for some memory leaks, so he could fix them. I am wondering if something like this could be possible now with other maintainers?
Thank you!
Hello! I'm a total svelte 5 convert, and have also been loving snippets and am using them a great deal. I'm wondering if there's a way to comment "top-level" exported snippets that I expect other areas of my app to consume. For example:
<script lang="ts" module>
export { reusable };
</script>
{#snippet reusable()}
...
{/snippet}
My attempts to add comments to the export aren't picked up by the language service, nor am I able to assign the snippet to a new variable + comment & export that (I get a Block-scoped variable 'reusable' used before its declaration.
typescript error).
I think it'd be ideal to be able to comment snippets in the markup similar to how components are done, e.g.
<!-- @snippet ...documentation... -->
{#snippet reusable()}
...
{/snippet}
Thanks!
I am using svelte-dnd-action
, and recently I asked the author if there's any plans to switch from Actions to Attachments. The author commented as titled, as well as pointing out that Svelte already provides fromAction
for people who need it, though they aren't sure why would anyone need to do so in the first place.
So if a library exist before Attachments and works fine using the Actions system, are there any benefits for library authors to switch from Actions to Attachments?