rich_harris
Honestly, I didn't expect the release to generate much conversation at all. There's a lot less focus/interest in front-end frameworks generally these days, for obvious reasons, so I figured the median reaction would be 'oh, the Svelte team are still shipping? good for them' and not much more. It's a joy to see the conversation here whether it's from happy users, or people who prefer different things, or people who have fully outsourced the writing-the-code bit to LLMs and straight up don't care about the underlying tools any more.
Which I totally understand! As framework authors we're very persnickety about the details of the code, so we've been on the slower end of the LLM adoption curve, but even we're starting to delegate more of the work to agents. If you're prompting an app into existence it's natural not to care that much about the particulars. But a question I've seen several times here, and in the zeitgeist more generally — 'in a world of agents why should I care about Svelte vs React vs whatever?' — deserves an answer.
And it's this: your app will be _better_ if you use Svelte. Your JavaScript bundle will use fewer bytes, your server-side rendering will take fewer milliseconds, and your users will reap the benefits: faster, more efficient apps. For all that the agentic revolution has created a huge _quantity_ of software, the _quality_ of the average app stubbornly refuses to budge. If anything, software is becoming less reliable. I think we've all felt that. Using a framework that treats things like accessibility and progressive enhancement as core concerns will help you steer towards better outcomes. (Your agent will use fewer tokens as well.)
Of course, this was always the pitch! Agents don't change that. And that's why we're still shipping and still sweating the details. We have some stuff coming up that we're unreasonably excited about — we really care about this stuff, and we're very grateful to be part of a community who shares that passion. Thank you for the support, it means the world to us.
For now, we have add-ons for stuff like drizzle and better-auth (https://svelte.dev/docs/cli/overview) which makes it relatively easy to set up these things without committing the framework itself to a particular set of choices. But I totally agree that a lot of the time it's nicer to just not have to think about it. Our current focus is on laying foundations (async Svelte, remote functions, custom renderers etc) but it's very much on our medium term radar.
I think about the woman in this story — https://shkspr.mobi/blog/2021/01/the-unreasonable-effectiven... — as an industry, we've lost our way if we don't care enough about people like her to make our apps work reliably. Not everyone has JavaScript: https://www.kryogenix.org/code/browser/everyonehasjs.html
Moreover, validation is something that belongs on the server. Client-first approaches to form validation are at best duplicative (because you need to repeat the validation on the server) and at worst dangerous (because it tricks you into thinking that's unnecessary).
I also notice that one of the first forms on your website doesn't adhere to common accessibility guidelines — the email field is marked invalid as soon as you start typing. Ordinarily, you shouldn't validate a field until it has been blurred.
So what I'd like to see from people building form abstractions is a) a full stack approach, b) progressive enhancement, and c) adherence to accessibility guidelines.
FWIW this is how we think about forms in the Svelte project: https://svelte.dev/docs/kit/remote-functions#form
React, needless to say, languishes at the bottom.
Will send a PR to fix the Svelte implementation.
has that happened with Svelte?
> they now have a monopoly over the most popular frontend meta frameworks (Next, Svelte/Kit, Nuxt, Astro)
this would be a surprise to the Astro team!
FWIW you're describing a legacy syntax — modern Svelte has explicit effects, and addresses your other concerns. I think you'll find it's much more to your liking https://svelte.dev/docs/svelte/$effect
This document is a text version of a talk I gave there, which expands on some of these ideas: https://www.youtube.com/watch?v=eswNQiq4T2w&t=5211s
It's deliberately brief and vague in parts, because it's designed to spur conversation. It's not set in stone, nor is it an Official Statement on behalf of the Svelte team — it's an attempt to articulate the way that the maintainers tend to find ourselves thinking about some of these topics, as viewed through my personal lens. If it helps explain why you like Svelte, great! If it helps explain why you hate it, that's great too — that's the whole point. Have fun with it, and if there are parts you disagree with then your homework is to think through what kind of tenets would describe your ideal framework.
Also, it's Saturday — get off Hacker News :)
In React, the component is the unit of re-rerendering. MobX can't change that fact. The only thing you can do is work around it with hacks that imperatively update the DOM.
But anyway: it's absurd to compare this with React+MobX. MobX replaces useState, sure, but you're still re-rendering entire components on each change (which is why MobX explicitly recommends that you break apart your app into many small components, regardless of whether that's a boon to readability and maintainability.
By contrast, Svelte (and Solid, and other frameworks) understand signals on a much deeper and more optimal level. They're really not the same thing at all.
Nevertheless, it's interesting and validating that we landed on such similar approaches. While the Reactivity Transform failed, it did so for reasons that I don't think apply to us:
- $state and $ref are quite different. $state doesn't give you access to the underlying object, so there's no conversion necessary between reactive variables and ref objects (either in your head or in code).
- There's strict read/write separation. Anyone with access to a ref has the ability to change its value, which definitely causes problems at scale. It's something that React, Solid and Svelte 5 get right.
- Reactivity Transform introduces things like $() for magic destructuring and $$() for preserving reactivity across boundaries. We're instead encouraging people to use familiar JavaScript concepts like functions and accessors
- There are already a lot of different ways to work with Vue — SFCs vs non-SFCs, template syntax vs JSX, composition API vs options API, `<script>` vs `<script setup>`... on top of that, adding a new compiler mode that needs to interoperate with everything else is inevitably going to be a challenge. This isn't the case with Svelte. While both runes and non-runes mode will be supported for the next two major versions, meaning there will be some short term fragmentation, we've made it clear that runes are the future of Svelte.
function createSignal(initial) { let value = $state(initial); return [() => value, (v) => value = $state(v)]; }
Note that the Solid change is _not_ 'one step' — the `completed` property is being turned from a property to a function, which means you must update all the usage sites as well. Using getters and setters also allows you to use Svelte's convenient `bind:value` approach, which is much less verbose than using the equivalent event handler code. And don't get me started on the [type narrowing issues](https://www.typescriptlang.org/play?#code/C4TwDgpgBA4hzAgJwD...).
There's nothing _wrong_ with the Solid approach, but the ergonomics aren't to our liking. If we need to take a hit in terms of verbosity, we'd rather do it once at the declaration site than n times at every usage site.
Yes
> Will the end result see temporary, half-updated values?
No. It uses a push-pull mechanism — dependency changes don't result in a re-evaluation until something asks for the value, meaning derivations are 'glitch-free'
This is one of those things that's more of a problem in theory than in practice, but nevertheless it's worth mentioning that Svelte 5 output is _much_ smaller than Svelte 4 output.
But one of our goals was for you to be able to use Svelte's reactivity inside .js/.ts files, since that's one of the things people have been crying out for since we released Svelte 3. For that to work, reactivity has to be opt-in, not opt-out.
And that's how it _should_ be — someone reading the code should be clued into the fact that this `let` won't behave like a normal `let` in JavaScript, and it should be possible to move code between modules (and between modules and components) without worrying about whether a specific file was opted in to certain behaviour.
In other words this...
> This way, the change wouldn't break existing code
...isn't quite right — it would break _all_ your existing code that wasn't in .svelte files.
> If I'm not mistaken, the compiler allows Svelte to define its syntax to anything they want.
On this point specifically: unfortunately not. The code in a .ts file, for example, has to be valid and typecheckable. You can't insert a preprocessing step ahead of the TypeScript compiler. Again though we concluded that this is a good thing, since it means this all works with all existing ecosystem tooling.
type Reactive<T> = T; function $state<T>(value: T): Reactive<T>
...then TypeScript will 'unwrap' the type anyway, unless you do funky stuff like this...
type Reactive<T> = T & { [uniquesymbol]: any };
...in which case things like `value += 1` will cause type errors because it coercies `value` from `Reactive<number>` to `number`.
But it also creates problems here:
let message = $state('hello'); obj = { message };
The type of `obj.message` is Reactive<string>, but it's _not_ reactive — it's a non-reactive snapshot of the value when the object was created.
It's possible that we can do some fun stuff with TypeScript plugins, but we haven't dived too deeply into it yet.
In Svelte 3, you had to opt in to running code on updates, using things like the `$:` label. It was designed to be conservative about updating components, unlike virtual DOM solutions that like to update everything unless you opt _out_.
I too am very sceptical of benchmarks — they can certainly obscure more than they reveal at times. But you don't have to take my word for it, or the benchmark results showing that Svelte 5 is faster than every other framework in existence (https://twitter.com/Rich_Harris/status/1688581184018583558) — you just need to understand the different mechanisms in play.
Basically, you can only modify state _where it's declared_. If you want to allow the 'outside world' to modify that state, you need to expose a function that does so. This is unlike cases where you're passing around an observable object where anyone with a reference, which they need for reading, can also write to it by doing `thing.value += 1`.
This is something Solid gets right — you can only change a signal's value if you have a reference to its setter.