These posts are aggregated from Svelte GitHub Repository.
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/useTransitionIf 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.byWhile 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!
I know a lot of appeals for this problem have gone unheeded in #6629. I'm hoping to start a reasoned discussion about it, as I believe the current warning is actually harmful for a11y in 2026.
The Svelte compiler will warn you if you include the autofocus attribute on any element.
<input autofocus>
<!-- ^ "autofocus bad" (not actual warning message) -->
One exception is when the element is a descendant of a <dialog> element, thanks to #16341:
<dialog>
<input autofocus> <!-- ok! -->
</dialog>
Unfortunately, this doesn't work for component composition:
<MyStyledDialog>
<MyCustomInput /> <!-- MyCustomInput.svelte will get the warning if it applies autofocus -->
</MyStyledDialog>
Descendents of elements with the popover attribute will always display the warning.
autofocusSvelte actually includes some logic to call element.focus() on mount:
/**
* @param {HTMLElement} dom
* @param {boolean} value
* @returns {void}
*/
export function autofocus(dom, value) {
if (value) {
const body = document.body;
dom.autofocus = true;
queue_micro_task(() => {
if (document.activeElement === body) {
dom.focus();
}
});
}
}
So, Svelte's position on autofocus is flawed: on the one hand, it tells you not to use it and, on the other hand, it helps you if use it. My first suggestion would be to deprecate this logic and remove it in Svelte 6. Here's why:
autofocus use cases: top-layer components (currently popovers and dialogs) will handle autofocus correctly$effect() runeautofocus on non-top-layer elementsMy personal preference is that the warning would go away. Dialogs and popovers with interactive content absolutely should have an element with autofocus. It would actually be better if Svelte could warn when this isn't the case (but I know that would be quite complicated to implement). I also think that, as a developer, I should not receive—and have to go out of my way to disable—warnings for what is essentially a good practice.
If it helps, <dialog> attained universal browser support in 2022, popover in 2024. There are options for progressive enhancement implementations of these. It no longer makes sense to flatly discourage all uses of autofocus.
If the warning is here to stay, then I think the documentation should be updated to tell the developer to disable the warning and use autofocus on descendants of <dialog> or <xyz popover>. It should not tell them they are doing something wrong for their legitimate use cases.
Is there an idiomatic way to do this? basically i have state, then I have some derived state but it's not pure - it's stateful too. here's a basic example
class Counter {
counter = $state(0);
}
class CounterWithAdded {
counter = $state(0);
sum;
constructor(props: { add: number }) {
this.sum = $derived(this.counter + props.add);
}
}
const a = new Counter();
// no, this will reset internal counter every time
const c1 = $derived(new CounterWithAdded({ add: a.counter }));
// recommended approach but wieldy when you have several options like that
const c2 = new CounterWithAdded({
get add() {
return a.counter;
},
});
// works and less verbose when a lot of properties probably but feels icky
class AddProps {
add = $derived(a.counter);
}
const c3 = new CounterWithAdded(new AddProps());
as for the real use case - we're a bank and we have a lot of functions built around svelte 4 stores where a function sets up a bunch of state, and then reused by other functions which derive from its state but declare its own state too and so on. when migrating this to runes - it's not very clear whats a nice, clean solution
it may look like this
function userCardsStore() {
const cards = writable([]);
return { cards };
}
function userCreditCardsStore() {
const creditStatus = writable('new');
const cards = userCardsStore();
const creditCards = derived(cards.cards, ($cards) => $cards.filter(isCredit));
function updateCreditStatus() {
if (get(creditCards).length === 0) {
creditStatus.set('empty');
} else {
creditStatus.set('with-cards');
}
}
return {
creditCards,
creditStatus,
updateCreditStatus,
};
}
assume creditStatus cannot just be a derived and since it needs creditCards, we can't just split these stores - it kinda has to be a stateful derived. And it's a big app so we can't be changing the logic much here so of course ideally there should be a straightfoward way to move store code to rune code.
I guess the 4th option is to introduce some Rune type { current: T} - akin to what fromStore returns and use that instead of Readable/Writable everywhere
This probably has been asked before but kinda hard to search for
This is a duplicate question from Discord: https://discord.com/channels/457912077277855764/1489264519468552294
Hello, thanks for the amazing works.
I try to understand how the keyed each block works under the hood by reading the doc
My understanding so far when there is no key given and causing the bug in the example of the doc:
When we remove the first’s item in the array, Svelte removes the last DOM node, then updates the remaining nodes.
Here is what I’m confused about, why only the text got updated but not the icon/src?
Apparently if Svelte did some comparisons for the src, it is the not the same.
As we removed the last item, I understand that the index remains the same and thus Svelte might thought nothing changes, but in this case, doesn’t the text also be the same make more sense?
I try to understand how it works instead of just following the doc where adding the key will definitely solved the problem.
something like this : playground
let set: Set<string> = $state(new Set());
it doesn't work but is there a way to make it work
Inspired by @MathiasWP's story 👏, decided to share this here.
I scored this repo for AI-Readiness and it got 38%, so I built a tool to improve the score, and now it gets 100%. The two files are below — now AI knows what I'm working on and is optimized for my exact project.
# sveltejs/svelte
project:
name: svelte-monorepo
goal: monorepo for svelte and friends
main_language: TypeScript
type: frontend
frontend: SvelteKit
# sveltejs/svelte
project:
name: svelte
goal: Cybernetically enhanced web apps
main_language: TypeScript
type: framework
framework: svelte
stack:
frontend: SvelteKit
state_management: Runes
backend: SvelteKit
api_type: Server Routes
runtime: Node.js
build: Vite
cicd: GitHub Actions
human_context:
who: Svelte core team and open-source contributors
what: Compiler-first web framework — components compile to vanilla JS with no runtime overhead
why: Web development should be simple, fast, and accessible to everyone
where: npm (svelte, @sveltejs/kit), used in production worldwide
when: Created 2016 by Rich Harris, Svelte 5 (Runes) released 2024
how: Open-source, MIT licensed, community-driven with full-time maintainers
Due to the Discord Moment, now it the right time to join Matrix - open-source Discord alternative with end-to-end encryption support.
We created #Svelte:matrix.org and welcome anyone to join it. The Svelte contributors are getting high ranks automatically.
The bridge with Discord is also possible.
Hello, first I'll explain my aim because the solution I am after might not be the best one.
Basically I have a classic set of components for integrating with MapBox. I have a Map component and MapMarker components that I put inside the Map. Then my markers would need a popup when I click on them. The good thing is that what is inside the popup is entirely based on a component I already have because it is used somewhere else on the website.
Therefore my gut feeling was that it would be ideal to put the content of my popup in the snippet of the MapMarker. In other cases, I would have been able to use the snippet easily because it is in the children prop. However in this case, I have to pass the HTML to a javascript function setHTML, and I have no idea how to use the children prop in this scenario. In the component it uses the @render tag. I have tried to just use the children as a function but I get an error saying it cannot be called outside of the @render tag.
I kind of can understand why. At the end of the day, the children is not just some piece of simple html, otherwise it would be rendered with @html instead. There is reactivity and all, and I might loose the scoped CSS, I get that. For my use case, having to re-inject the style in a way or another and have html created at mount time would be acceptable. If anybody has an idea of how I could achieve this, I'd love to hear about it.
Thank you in advance !
Sharing a tool I built for automated WCAG 2.1 testing that works well with SvelteKit apps.
npx accessscore https://your-sveltekit-app.com
Runs 16 WCAG 2.1 Level A/AA checks and outputs your score, grade, and issues.
| Site | Score | Grade | Issues |
|---|---|---|---|
| svelte.dev | 87 | B | 10 |
| nextjs.org | 100 | A | 1 |
| react.dev | 82 | B | 14 |
| tailwindcss.com | 73 | C | 48 |
svelte.dev scores 87/B — solid but there's room for improvement (missing skip nav, heading hierarchy, some link text issues).
Document language (in app.html):
<html lang="en">
Skip navigation (in +layout.svelte):
<a href="#main" class="sr-only focus:not-sr-only">Skip to content</a>
<nav>...</nav>
<main id="main"><slot /></main>
Image alt text:
<img src={product.image} alt={product.name} />
Form labels:
<label for="email">Email</label>
<input id="email" type="email" bind:value={email} />
Svelte already has built-in a11y warnings in the compiler (which is great!), but automated runtime scanning catches issues that static analysis misses — like missing alt text in dynamically rendered content.
Full tool: accessscore.autonomous-claude.com