Also, I will never understand why people like reactive signals. The article even quotes "Knockout being right all along" which, no, reactivity and two way data binding creating a spaghetti mess of changes affecting other changes all over the place unless you're really careful is why React was made in the first place, to have only one way data binding and to re-render the entire page in a smart way. React will soon get its own compiler as well which will made regenerating the DOM even more efficient.
It's like every other framework is slowly rediscovering why React made the decisions it made. I am assuming it's because the users who are making these frameworks now have not used or do not remember the times where "signals" were called observables, or the usage of Rx libraries, and how having too many of these would cause you to lose your mind debugging the intricate webs you inadvertently spun.
That's the part Knockout was right about. It's absolutely true that you can mishandle them and create a spaghetti mess, _if your design allows it_. Svelte 5 doesn't — it uses signals as an implementation detail, but in a way that prevents the sorts of headaches you're describing.
Signals and observables (e.g. RxJS) are related but separate concepts. The framework world is converging on signals as the appropriate mechanism for conveying reactivity — even Angular (which has historically been most closely tied to RxJS) is moving in this direction. I promise you it's not just a mass delusion.
You call signals an "implementation detail", but if someone doesn't know that's what they are, then they'll wind up doing very bad things. This means they either aren't just an implementation detail or they are a hyper-leaky abstraction.
Care to elaborate or do you have a link to a blog post explaining further?
I was just mentioning in another comment how Rust has a similar problem where, as in C, you can mutate variables via pointers anywhere in the codebase, but Rust counters that by having a borrow checker that explicitly tracks the changes made and makes sure that only one user can modify a variable at any one time. An analogous concept might be quite interesting to implement in all these signal based frameworks as well.
Which is what Svelte 5 is doing with its compiler.
> […] given a large enough codebase, that sort of spaghetti mess will emerge on its own, as not everyone will be so thorough.
LOL! You've just described React projects! Yes, of course you and your team are wonderful and have all the FP experience, so obviously this isn't an issue for YOU.
Most React codebases however turn into big balls of mud. Enormous balls of useMemo-y, useEffect-y, re-render the world-y, spaghettified mud without even any compiler-enabled guardrails that get re-written every 18-24 months but "this time we'll do it right."
I know its not anyones fault (RxJS is amazing), but it does seem to invoke some gray area in the brain, thinking observables would do more heavy lifting around change tracking of inputs / sources, when its not that straightforward. They more rightly should be called SubscriptionStreams maybe
I think its always been somewhat poorly named, based on how often I've had to explain the concept to other developers.
Also observables are more like streams than arrays.
On the other hand, on the other side of the learning curve, Observable is a great name. It's a simple English word that you can say and write a million times (and you will need to when working with them) without extra PascalCase or things like that. Also, when you look at the overall "family" that Observables fit into, it is a name that fits the family: Iterable/AsyncIterable ("PullStreams") versus Observable ("PushStreams"). (In .NET LINQ the family uses Enumerable/AsyncEnumerable and also extends to Queryable and the strangely named but makes sense in context Qbservable. [QueryableObservable; often pronounced like "cube-servable".]) As a family of tools that all generally work well together and are essentially related in a "four quadrant" way, it helps that they all have a nice -able names that sound related.
> why React was made in the first place, to have only one way data binding and to re-render the entire page in a smart way
This last part is why Vue and now Svelte didn't adopt React's model. Yes, reactivity and two-way data binding give you enough rope to thoroughly hang yourself, but it also gives you a very explicit picture of how and when components will update. React, on the other hand, expects you to trust its smart algorithms, which definitely removes a lot of the pain from day-to-day coding but also makes troubleshooting performance problems harder.
I see the distinction as similar to GC languages vs something like Rust: if all you need is to have an app that does a job and you don't care about how it does it, React is a good choice. If you need to have control over exactly what will update and when, Vue's model gives you fine-grained control over updates that's hard to achieve in React.
EDIT: And with that in mind, it's obvious why Svelte, which prides itself on being lean and fast, would avoid the React smart-but-complicated runtime model in favor of the Vue model.
Most devs are of average talent and will hang themselves and everyone else with the stateful webs they weave. Even with careful PR reviews, these things have a way of sneaking in and becoming permanent hinderances.
Performance hasn't been a dealbreaker in most JS apps for years now. What HAS been a problem is getting good code out the door as fast as business needs. MOST React code is disposable based on the whims of the company. The bits that aren't disposable are used enough to justify having them designed and maintained by your best devs who should also have the skill to write performant code.
Performance does matter to the people who depend on our products. Can we please stop throwing people under the bus in the name of churning out more crap?
I know you're right; but this still deeply saddens me. I want well crafted software. I want the code I write and the code I depend on to be made in a way that holds reverence and honor for the craft of software engineering.
I don't want to be running crappy, badly performing javascript made by average talent in react, that exists to serve someone else's transient business needs. Gross.
It makes it straightforward to reason about, because you aren't dealing with globals, undeclared deps etc. like in the Knockout days
Performance was the main issue because one incorrect usage of a hook can make a huge difference.
Static analysis seems like a nice to have. All in all I class this category of concern as very far down the list of concerns, one way or two way. Yes bad things are possible. But the martial attitude that oh no people will do it wrong this tech is horrible is, in my view, actively harmful.
99.99999% of times it goes great. We don't need to make coding environments were bad things are impossible.
It was a bit sad that it didn't get more popular and possibly improved. Some devs liked it but we were a minority compared to those who liked/used Redux.
One strange thing they did was to release a new "version" called Mobx State Tree which was very different. It had a great api but was different and slower.
For Svelte as an "island" tool (i.e. for building islands of interactivity in otherwise static pages, like, say, graphs for the New York Times), that's not a problem, because the extra generated code is made up for by the lack of a bulky runtime dependency. But if you scale that up to complicated SPAs - which has been Vue's main stomping ground - you can end up shipping larger files with Svelte than with other small frameworks.
That said, SFCs in Vue are an ideal source for compile-time compilation, and my understanding is that they're heading more down the route of SolidJS. SolidJS has a similar goal of using a compile step for optimisation, but it leans on its signals library implementation much more heavily, which means the compiled code tends to be much smaller, but you also need to include a runtime. (In practice, there's not much size difference between Svelte and SolidJS at all, but it shows itself at the more extreme ends of very small components and very large apps.)
Vue already has a runtime that's very similar in some ways to Solid's (read: it's all signals, baby), so adopting the rendering ideas from there is an obvious next step. That should drastically speed up rendering, as well as reducing bundle sizes significantly. They've already demoed this a bit, but I think it's not fully released yet - they gave a name for the new system, but I've forgotten what it's called.
Eg if a form input changes, that change triggers an onChange and in a handler function, you can let the change just flow through the data layer (ie through a signal or a store, which is just a hierarchy of signals). This then updates the input, but it already had that value so nothing happens. It’s pretty much the unidirectional loop that React people love to talk about.
It’s not the kind of problem you can appreciate when taking any framework for a spin with a form input.
Nope. More like every other framework is slowly rediscovering that observables and dependency tracking offers the best DX and most of the innovations that React introduced have better alternatives.
though, the SSR madness movement including React concerns me, everyone tries to mix CSR and SSR into one now, which makes things way more complex than it needs.
I get it, it gives you flexibility to hydrate the view as close to the user, as late as possible. That sounds cool, but I wonder how many folks really use it and how much you pay for not clearly defining where and when each bit is happening.
Definitely there is convergence in DX now. No need for everyone to admit one or another framework was right first or not - that is just creating antagonism or negating people lived experiences and innovation. But I do find it pretty weird that this convergence is happening. Initially I thought it was diverging with svelte but it seems to be reserving direction.
Convergent evolution [0]. Just as a shark, dolphin, and ichthyosaur all have the same body shape which is efficient for swimming and hunting in the water, so too do technologies converge when the problems are all the same and the solutions are understood, only as humans we can learn from each other rather than randomly mutating our codebases [1]. It's the same reason why we see a lot of languages starting to adopt more functional features such as not using `null` and having exhaustive pattern matching, such as Rust (from OCaml), Dart 3, recent versions of Java, etc.
In Svelte's case, it feels like they went as far as they could without directly addressing observables until they reached a point where it became one of the largest features that had yet to be addressed.
Open source frameworks, not unlike companies, need to continue attracting new users to grow the ecosystem, which means pleasing an ever-growing user base.
autocorrect style typo, i think
Honestly never understood this - 2 way is less code and way more understandable to me. Redux style codebases are very difficult for me to understand, and never saw benefits praised for. More frequently was "why isn't this updating?".
Different strokes I suppose.
With proper memoization, this is both fast and ergonomic, and it even allows you to turn mutations into data and pass them to a server. I've implemented this as a stand-alone state helper [1] based on JSON patch/diff, and it's magical. Undo/redo, multiplayer, it's all within reach.
This will be amazing if works as people are hoping it will. I suspect it's an impossible task (in the general case). My hope is a certain coding style will be the path forward, and not a new version of putting shouldComponentUpdate (and others) all over the place.
You need architecture to avoid a big ball of mud or spaghetti code. Organising your code in layers, modules, each with its own API. Rules which layer can access what other layer. Like backend does for ages. You can and should do this in large frontend SPAs as well. No matter if written Svelte, Angular or React.
And also a tendency to not have experiencing working on large web apps (I’m talking 100k+ cloc) where issues arise, and why those React engineers made the decisions that they did
For me they're a dealbreaker when considering a new framework.
Nope, nope. Been there, done that, with 2-way data binding and never going back.
We used to know that, too. We also used to know that entirely event-driven architectures were a level of chaos you shouldn’t invite into your enterprise. Don’t need to look hard to see those either.
Whole industry has been cycles of collective amnesia going back to at least 1975.
[Edit] but in this case what we seem to be doing is having another go at Aspect Oriented Programming, which I was interested in for some time but concluded it, like event-only systems, are a poor fit for the average developer’s mental model, and also a terrible way to encode business requirements. Particularly from a testing perspective. They are usually dabbling at functional core, imperative wrapper architectures, but tend to retain global shared state which is a reliability nightmare waiting to happen.
> Like every other framework, we've come to the realisation that Knockout was right all along.
> Svelte 5's reactivity is powered by signals, which are essentially what Knockout was doing in 2010. More recently, signals have been popularised by Solid and adopted by a multitude of other frameworks.
> We're doing things a bit differently though. In Svelte 5, signals are an under-the-hood implementation detail rather than something you interact with directly.
That's a sad development and further makes the framework more complect (as defined by Rich Hickey), with more hidden layers and "magic".
But if these guys are only now reaching Knockout, I still have to wait for them to catch up to Backbone.js, I guess.
I find myself feeling this way a lot while writing front-end code.
While I am primarily a back-end developer by trade, I find myself working in Vue or React quite often just to get things done and regularly come to the realization that the majority of the reactivity in the projects I am working on is either unnecessary or so simple that it would be handled with less complexity as a line or two of jQuery.
It works but on longer projects where requirements can explode in complexity it becomes a problem. It really helps to have tools that can stay maintainable.
https://w3techs.com/technologies/overview/javascript_library
I don't love knockout, but I find it easier to debug than react. Mainly because stuff is synchronous, meaning the thing that caused the change is above the problem in the callstack. In react, a lot of stuff goes through an async work scheduler, so the originator of the problem is long gone.
If anything it makes it clear what's going on and when, which is important for the kind of stuff I do which has a lot of interconnected components which are constantly refreshing.
let count = $ref(0)
const double = $computed(() => count * 2)
watchEffect(() => {
console.log(double)
})
We started experimenting with this ling of thought almost 3 years ago:- First take (ref sugar), Nov 2020: https://github.com/vuejs/rfcs/pull/228
- Take 2, Aug 2021: https://github.com/vuejs/rfcs/pull/368
- Take 3, Nov 2021: https://github.com/vuejs/rfcs/discussions/369
We provided it as an experimental feature and had a decent number of users trying it out in production. The feedback wasn't great and eventually decided to drop it. https://github.com/vuejs/rfcs/discussions/369#discussioncomm...
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.
I wouldn't say they are "different" - they are fundamentally the same thing: compiler-enabled reactive variables backed by runtime signals! But yes, Vue already exposes the underlying concept of refs, so for users it's two layers of abstractions. This is something that Svelte doesn't suffer from at this moment, but I suspect you will soon see users reinventing the same primitive in userland.
> There's strict read/write separation
I'd argue this is something made more important than it seems to be - we've hardly seen real issues caused by this in real world cases, and you can enforce separation if you want to.
> We're instead encouraging people to use familiar JavaScript concepts like functions and accessors
This is good (despite making exposing state much more verbose). In Vue, we had to introduce destructuring macros because we wanted the transform to be using the same return shape with all the existing composable functions like VueUse.
> There are already a lot of different ways to work with Vue
This is largely because Vue has a longer history, more legacy users that we have to cater to, so it's much harder to get rid of old cruft. We also support cases that Svelte doesn't account for, e.g. use without a compile step. That said, the default way with a compile step is now clearly Composition API + <script setup>. Reactivity Transform also only really applied in this case so the point you raised is kinda moot.
Separate from the above points, the main reason Reactivity Transform wasn't accepted remains in Runes: the fact that compiler-magic now invades normal JS/TS files and alters vanilla semantics. Variable assignments can now potentially be reactive - but there is no obvious indication other than the declaration site. We had users trying Reactivity Transform on large production codebases, and they ended up finding their large composition functions harder to grasp due to exactly this (and not any of the points raised above).
Interestingly, the new changes could be seen as a retreat from that initial invasion, likely triggering a different response from the community. In fact, the resistance I've seen (and my own as well) has been in the opposite direction—it's hating this retreat, complaining Svelte becoming less "magical." and more close to regular joe Javascript.
It’s intriguing to see where the Svelte exploration will lead. Will it face disapproval or achieve success? While I agree that both implementations have arrived at the same place (almost serendipitously), their origins differ. For Vue, it's about adding the syntactic sugar by dropping .value, making it less explicit and more magical. In contrast, Svelte made the change to make things more explicit and reduce the “black magicness” from the svelte compiler, and bring it more similar to javascript. This difference might trigger totally different reaction, time will tell.
Looking forward to more insightful discussions!
Shhhh. Don't tell the Vue/Svelte community that they're just reinventing the React ecosystem on a 5 year delay. It will spoil all of their fun.
1. primitives for managing state - $state vs useState
2. removal of lifecycle mechanisms - onMount vs componentDidMount
3. replacing lifecycle mechanisms with new primitives - $effect vs useEffect
It's like the Svelte team took a leaf out of React team's book on how to upgrade a framework - this is evident by the way these features are presented as opt-in like how React marketed Hooks as opt-in. I would go on to predict that the upgrade to Runes will just like the upgrade to Hooks. Developers will use it and then love it - because it presents improvements to the way codebases will be structured and maintained just like React did with Hooks. This is really a Hooks moment for Svelte. Good job Rich and the Svelte team!
<script> let time = $state(new Date().toLocaleTimeString());
$effect(() => {
const timer = setInterval(() => {
time = new Date().toLocaleTimeString();
}, 1000);
return () => clearInterval(timer);
});
</script><p>Current time: {time}</p>
Example: https://www.solidjs.com/examples/counter
`onMount` and `onDestroy` feel like really useful, dependable callbacks. `$effect` is scary because if you add a reference to state in it, you can't depend on it being called like `onMount`.
They claim most (but not all) of current usage of `onMount` will be better off using `$effect` instead
But I fell in love with Svelte because it was dead simple (according to me). It was a breeze of fresh air and I felt I just program again without wiring enchantments together.
I do agree that its simplicity has downsides too, so perhaps it's just nothing to be afraid of?
But I can't help seeing this as ominous:
> This is just the beginning though. We have a long list of ideas for subsequent releases that will make Svelte simpler and more capable.
Please don't turn Svelte into a black magic box.
I've got a couple of thousand hours of Svelte dev experience, and these changes offer me nothing I really want.
Svelte dependencies outside my .svelte files? No thanks, I'll keep them usable in non-Svelte projects.
A new way to do reactivity to try to appeal to React users? ("When people come to our community in future..." in the vid.) No, thanks! Svelte 3/4 reactivity was very straightforward, one could learn the whole thing in a day from the excellent tutorials. It was better than React.
There was definitely a bit of weirdness with overuse of reactive vars but that's been incentive to keep components small and simple. A good thing!
Personally I'm still stuck on v3 because 4 introduced breaking changes I haven't had a chance to debug, so it'll be a while until any of this impacts me anyway.
Maybe I just need to try it out.
Maybe it's because of using the "runes" word :)
But watch the introduction video. It isn't at all like the functional reactivity that entered other frameworks. It's still the svelte way. Read and assign is plain javascript. Only the initialization syntax changed.
They even promise backwards compatibility, so you don't have to migrate or migrate file by file if you want to. Which is also not how other frameworks handled this in the past.
When you had this:
let count = 0;
count += 1;
… it made reasonable sense, because that’s just normal JavaScript; the fact that `count` was made reactive was basically incidental.But once it’s this:
let count = $state(0);
count += 1;
This looks like you’ve called a function named $state, and given that you’re talking about migrating from compile-time to runtime reactivity, you might (I think reasonably) expect `count` then to be some object, not just an integer, and so `+= 1` would be the wrong (since JavaScript doesn’t let you overload those operators). But no, it’s instead some kind of decorator/annotation.Yes, stores are unwieldy as you scale them up, and definitely have some practical problems, but that createCounter stuff looks fragile. I’m curious how it all works, because it looks like it’d be needing to do quite a lot of control flow analysis, but not curious enough to investigate at this time. But my intuitions suggest it’s probably markedly more complex and difficult to explain, though perhaps and hopefully more consistent.
Implementation-wise, this is vastly simpler than Svelte 4, because everything is explicit.
One thing we didn't really show today is how this works in your editor in practice — for example, TypeScript thinks `$state` and `$derived` are just the identity function. It makes sense when you use it, but I appreciate that I'm basically asking you to just trust me on that!
> Previously, Svelte’s reactivity model was very easy to understand, including its limitations, because it was the result of very simple analysis
I totally understand what you're saying. But in fact the analysis is _anything but_ simple — in places it's frighteningly complicated, to the point that even I sometimes don't understand what's going on! And that's part of the problem — because Svelte 4 is more implicit, you can't really know what's happening in a lot of cases, you just have to hope that the compiler knows what it's doing.
But at the same time I just wish there was some other solution to this while keeping it implicit somehow. I would do anything to keep things implicit while solving the backend complexity some other way. The thing I most love about Svelte is the principle / vector of "as little learning as possible", as well as in many cases transfer of learning, makes it much more user friendly, and I wish evolutions of Svelte continued to evolve along that direction, by reducing more and more.
But things like `$something` are a bit strange. Even `$:` is strange. It adds cognitive load. It's not very English-like and in turn not easily parseable. `on:click` is closer to the attractor of user friendliness. I think all programming languages and frameworks should try to approach English or Python, to allow for maximum "transfer of learning" so things just feel like they flow "without thought". Taking inspiration from UI/UX... things should be as close as possible to 'understanding at a glance'
The gold standard would be to approach something like this level of intuitiveness in the future as crazy as it may seem: https://twitter.com/brianjoseff/status/1617556877218570241 https://twitter.com/mathemagic1an/status/1700232760756207956...
I have many ideas on how, but it would no longer be a programming language.
That seems like a missed opportunity on Svelte’s part… but hard to fault, because TypeScript doesn’t support nominal primitive types very well. Ideally it would be something like Reactive<T> to signal (ha) that it’s not just a plain value.
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.
What is weird is that it’s actually typed as a svelte-compiler-only ‘extended int’ that can have side effects when you increment it, and weird knock on results from reading it.
For me personally I tried svelte in the past and bounced off because there was too much implicitly happening that I needed to have a deep understanding of to model correctly. This solves basically all those problems for me.
I thought your video[1] especially did a great job walking through the pros this change brings. Thanks for all your great work on this!
1. Link for the curious: https://www.youtube.com/watch?v=RVnxF3j3N8U&t=6s
Given this, I'm curious: couldn't the traditional syntax of `let counter = 0` be made to function similarly to the new let count = $state(0);? Transpile it to let count = $state(0) under the hood? If that can work technically, instead of introducing a new rune for reactivity, why not introduce a "negative" rune to denote non-reactive statements? This way, the change wouldn't break existing code; it would be more of a progressive enhancement.
I agree the move to unify the Svelte <script> tag with regular js/ts files is an improvement. It was indeed a little odd that certain syntactic sugar, like the $, would work exclusively within the Svelte <script> and not in a js/ts file that's right next to it. However, from what I gather, it seems the Svelte team is aligning the Svelte script more with js/ts, rather than bringing the js/ts closer to Svelte's unique syntax. This trajectory seems to be pushing Svelte towards resembling traditional JavaScript frameworks, like React. It's a departure from Svelte's unique strength of having a custom compiler and behaving more like a language. If every syntax in Svelte is expected to mirror its behavior in js/ts, eventually svelte will lose all it secret sauce that made it so unique. Why can't we add a rune into js/ts file, a note in the beginning to tell svelte compiler that this is svelte enhanced js/ts file, compile it like svelte script tag code? Bring js/ts more alike to svelte?
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.
I can't find an issue for that among top 50 open issues on Svelte's GitHub repo. I've also been a member of Svelte's Discord server for years and do occasionally hang out there, I haven't seen people "crying out" for this at all.
On the other hand, there are things like `<style module>` or `forward:class`, etc. that people have ACTUALLY been crying out for (literally one of the most upvoted issues in the repo) which haven't been addressed at all.
What if it is opt-out reactivity in .svelte files and opt-in reactivity in .ts/.js files? Yeah I know it would be a bit more combersome to copy code from .svelte to .js/.ts files but I think it would be worth it
Given the constraints with TypeScript not being further compilable, I've been pondering on Svelte's direction. Personally, I'd lean towards letting go of some of the special touches in js/ts file if it means keeping more magic in Svelte. If we're heading towards making Svelte syntax work exactly the same in js/ts entirely, it feels like we might risk turning Svelte from its unique language-like identity into just another framework in the JS world.
Thanks again for the insights! I have already felt a little better about my current work in migrating an app from another framework to svelte 4. I was worried that I have made a very wasteful decision for my own company.
exactly my thoughts, feels like they could've kept it much simpler
<foo bar="baz">{boop}</foo>
to a createElement function call: React.createElement("foo", { bar: "baz" }, ...boop);
Other than that it's entirely Javascript. No templates, computed/derived values or any of that other bullshit. You could probably implement the compile step in like 5 lines of code.I've built big projects in Vue and find it almost impossible to find a reason to use it. Svelte looks cut from the same cloth. They just fundamentally have the wrong approach.
A company I worked at had a O(1 MLoC) React frontend with no JSX at all for a long period of time due to the Typescript compatibility issue, just `export const El = React.createElement`
But since then, the front end community has discovered valuable new ideas and techniques. Meanwhile, people have encountered the limits of the Svelte 3 approach. Svelte is really good at solving most of the problems you throw at it, but it's not perfect. The changes we announced today are _necessary_.
If you're not convinced, I encourage you to watch this video, where I talk about why that is: https://www.youtube.com/watch?v=RVnxF3j3N8U
I built two Svelte apps. I liked using Svelte but was a bit annoyed by the aspects these changes are fixing.
The code also seems easier to write and to understand.
My feeling that the Svelte team has taste is strengthened.
Perhaps, just perhaps it's a good change for a lot of people?
Mobx was ahead of the game here (though, granted, it too draws on Knockout.js). You can use Mobx to declaratively describe reactive state, much like this. But it isn't integrated into any framework - you can use it in vanilla JavaScript with no other runtime or compilation required. You define models with Mobx, then on top of that there's mobx-react, which ties your model into React's update APIs (essentially making render() automatically observe values, and making relevant changes trigger a re-render), or there's mobx-vue, or mobx-svelte, or mobx-preact, etc etc. Decoupling this is super helpful - for example I'm using mobx to model raw state, computed state & reactions to changes in server side Node.js, no problem.
Meanwhile, recreating the same reactive concepts independently in each library instead makes all the resulting code incompatible, so even your pure JS state models are coupled to your UI framework - e.g. createCounter in this post has to import & use $state from Svelte. That makes it far harder to change frameworks in future, hard to share code between apps using different frameworks (plausibly even different versions of the same framework), etc etc.
I'd love to see a native common standard for this, similar to how promises were eventually standarized and suddenly everything had a compatible model for handling async future results (I could swear I've seen initial discussion on exactly that already, but I can't find it anywhere now sadly).
But not only it's an added layer of abstraction, it's also a complex piece of machinery which is not needed when the framework solves reactivity.
When I started using Svelte back in 2019 I was very happy to let go of MobX.
Mobx tc39 decorator support is pretty much done too. I check in on the PR weekly haha.
One huge sadness I have is that when you have a callback in .then or .next, your handler doesn't get a gratis reference to what you were listening to. I'd love to have this be a better record. DOM is so good about having thick events that say what happened, and it's so helpful, sets such a rich base, but js snubbed that path & left passing data in 100% to each event producer to decide as it will. Consumers can craft closures to seal some data into their handlers but it sucks compared to having an extensible data flow system. We're kind of sort of finally fixing this crime the hard way by standardizing async context/async_hooks, which is a whole new ambient way to create context that we can enrich our async events with, since there was no first class object to extend for having omitted passing in the promise to the promise handler. https://github.com/tc39/proposal-async-context
Also worth pointing out, maybe arguable as a hazard tale, observables was proposed a long long time ago. There's still a ton of adoption. But it also feels extremely similar yet notably apart & worse ergonomics than async iteration. It's imo a good thing it didn't get formalized. https://github.com/tc39/proposal-observable
Alas one reactivity I really wish we had had is object.observe (note: different API than observables), or something like it. Undoing good specification work & saying, 'yeah, if users want it, they can implement it themselves with proxies' has lead to no one doing it. And you can't just observe anything; whomever is the producer has to up front make the decision ahead of time for all consumers, which turned a great capability into something first boutique then quickly forgotten. Alas! https://esdiscuss.org/topic/an-update-on-object-observe
I ack your ask, and there's no shortage of options that are pretty damned good. But I feel like we are still in a discovery not deployment phase, still delay competing because we have a lot of terrain and idea space to consider. Before we pick, standardize & ship one.
This is the primary place where we have to go outside the framework in React. Only a large linear list has this problem -- if it was a decently balanced component tree, then we could skip updating huge swathes of it by skipping at a top level node. But it is not possible in an array. You have it iterate through each element and do a shallow comparison to see if the references have changed.
That said, it is fairly easy to escape to DOM from inside a React component with useRef and the portal pattern. Then we can write vanilla JS which makes updates to only the values it changes.
If Svelte solves this in an elegant manner, then it would be a very compelling feature over React. I'm asking here because last I checked, most of the examples in Svelte's documentation pointed to simple counters and regular objects, and I couldn't find an example of a large linear list.
But it doesn't help in the rendering phase - aka when the virtual DOM is constructed when `render` is called on the root component, and all our JSX code is executed across the component hierarchy. Virtual DOM reconciliation cost is only a part of the performance penalty, re-running the "view as a function of state" computation is another major chunk.
Svelte:
<button on:click={nextLight}>Next light</button>
Vue3: <button @click="nextLight">
Next light
</button>Svelte for Apps. Svelte for Sites.
I found `$:` to be extremely confusing, full of weird quirks, and completely turned me off to using svelte for anything more than basic sites.
Runes seem like a clear improvement, but brings Svelte a step closer to React -- which hurts its appeal to me.
The difference between `let counter = $state(0)` and `const [counter, setCounter] = useState(0)` is near its initial value -- zero.
I do love the view library competitive landscape and enjoy seeing different paradigms leveraging reactivity. I also like the compiler optimizations that Svelte is bringing to the entire ecosystem.
I wrote my initial thoughts on sveltekit when I built neovimcraft: https://bower.sh/my-experience-with-svelte-kit
> The magic of Svelte isn't `let count = 0`, it's `count += 1`
That part hasn't changed!
I'm guessing runes will make it easier for the compiler to track the reactivity which will enable stuff like automatic partial hydration? Maybe even something like qwik?
I only read the blog post and didn't watch the video... was there any mention on perf and size improvements?
And the output from the compiler is much smaller. You can compare the output on the Svelte 4 REPL to the output on the Svelte 4 REPL. The runtime has grown a bit, but the output overall is still much smaller even when factoring that in
That said, svelte5 does solve a lot of problems that stop me from trying it.
Does not Svelte from 2018 works today?
They got the DX right from the start, that other libraries are still trying to emulate.
Let's compare Svelte's and Solid's approach to nested reactivity. Both of them implement the same nested reactivity todo example:
Svelte: https://svelte-5-preview.vercel.app/docs/fine-grained-reacti...
Solid: https://www.solidjs.com/tutorial/stores_nested_reactivity?so...
In Solid, converting something to use nested reactivity is one step. In Svelte, it is two steps. And that second step is really verbose and annoying:
todos = [...todos, {
get done() { return done },
set done(value) { done = value },
get text() { return text },
set text(value) { text = value }
}];
Solid makes read and write segregation very simple and obvious. You don't need to manually make all these getters and setters.It is nice that runes allows nested reactivity in Svelte, but it feels nicer to use in Solid.
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.
1. It's really nice that it's so easy to make a `createSignal`
2. I didn't realize that in Solid you had to update all the usage sites too, so now I'd rather stick to the Svelte usage. Nested reactivity is not as effortless as I thought in Solid.
get done() { return done },
set done(value) { done = value },
get text() { return text },
set text(value) { text = value }
Looks a bit boilerplate-y maybe, but keeping your preoptimized call sites is totally worth it.This might be common enough that some syntax sugar might be worth it?
The short hand for:
todos = [...todos, {
get done() { return done },
set done(value) { done = value },
get text() { return text },
set text(value) { text = value }
}];
Could be: todos = [...todos, {
$done,
$set
}];”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.”
I now realize that this isn’t exactly the same thing as Vues ref of Preacts signal. Maybe its own name is good after all.
Vue also uses ”computed”, but ”ref” instead if signal. It just strikes me that a lot of frameworks seem to implement the same thing (signals) but use different names for it.
Their examples seem to be missing some imports? Trying to use $state as shown with svelte@5.0.6 gives "ReferenceError: state is not defined".
You can do it project wide or per component: https://svelte-5-preview.vercel.app/docs/runes
Opt-in is optional, no?
I think a real type system (i.e. compiler checked, rather than relying on falibilities of human programmers) would be a better solution. If Svelte already has a compiler why not implement this as part of it?
One thing that I am definitely happy to see is the removal of $: as it should help Typescript users. Personally, I was quite sick of writing:
let input = 'Hello';
// ...
let loudInput: string;
$: loudInput = `${input)!`;
Instead of: let input = 'Hello';
// ...
$: loudInput: string = `${input)!`;
It's an incredibly minor thing, but when you do it 1000 times it becomes very frustrating. Having reactivity be rune-based should help TS avoid the syntactic confusion, bringing us to: let input = $state('hello');
// ...
let loudInput: string = `${input)!`;> Well, no. The reality is that as applications grow in complexity, figuring out which values are reactive and which aren't can get tricky.
People keep re-learning that there's a certain amount of context that needs to be explicit, and you can't just imply everything. Just like when ruby and python made the mistake of getting rid of let/contst/var/etc and programmers said wait no that's a bad idea, now it just makes everyone's job harder, because neither the compiler nor the developer can figure out what context something belongs to.
It also comes with a built-in model layer called Stores which is genius in its simplicity. In React land I’ve used Flux, Redux, MobX, mobx-state-tree, zustand and pullstate, and IMO Solid Stores beats them all (because Solid makes this possible, not because those libs are dumb)
Also, I don't really understand why it's at the top of HN either, is this a groundbreaking change to whatever Svelte is?
Then came React -- which again changed the game for frontend web development. Instead of wonky scripts and targetting CSS classes, you get a modular and reactive approach in building the web. Then they started cursing React because of performance issues and implementation complexities.
Svelte was designed to behave like React but perform better and reduce the implementation complexities. I had the chance to work with Svelte 1 back then and as a React developer, it would really make you think "Why did React do that?".
This is probably on top of HN because people loved Svelte too -- but some followers are now questioning the direction as this change is gravitating towards solutions that React already implemented. As it happens, React did solve a lot of problems for the frontend, and they really nailed it.
If yes, how do you deal with two $derived sharing some dependencies?
Will the end result see temporary, half-updated values?
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'
Actually, it was Svelte who coined the term and sold us on the idea of reactivity by default. I don’t think anybody asked for “Reactivity by default”. Svelte advanced this idea, and it helped the framework gain traction. It was easy to get started, and gave Svelte this sense of better ergonomics than other frameworks. I was always skeptical about the performance claims, amortized, and the real selling point of Svelte was ergonomics and the dev experience.
The problem with the Node.js ecosystem is the devs are borderline marketing and sales type. They’ll justify, rationalize and make things sound good after the fact. Previously, Svelte was persuading us that Svelte was better than the rest because of reactivity by default. Now they did a literal 180. It’s probably in the right direction, and maybe how things should have been. A related symptom of the Node.js ecosystem is reinventing and rediscovering the wheel. The problem here is a lost of trust. Anything else which Svelte purports it’s got figured out or is more enlightened about should be taken with a grain of salt.
So it seems those boring FANG engineers with React has it right all along. They had experiencing building sufficiently complex apps where verbose but explicit code was necessary.
> Because the compiler can 'see' where count is referenced, the generated code is highly efficient
Yeah, I don’t believe such claims anymore. Sure, in cherry picked and constrained settings, the performance benchmarks might seem good. As much as I hate to admit, I will reach for the production ready and battle tested React, as boring as it is.
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.
let count = 0
def increment
count++
tag App
<self>
<button @click=increment> "Increment"
<div> "Count: {count}"
imba.mount <App>
Try this example here: https://scrimba.com/scrim/cpbmKzsq(Imba is a compile-to-js language that includes JS/HTML/CSS and a react-like framework all as part of the language. https://www.imba.io)
This is exactly what SolidJS already does, and SolidJS is #1 in almost all performance benchmarks.
One thing that popped out was that it seems like .js files will also need to be transformed now to accommodate the $ to rune translation.
Feature request to make that optional, perhaps something like:
import { rune } from "svelte/rune"
let $count = rune(0)
Oh, one other thing. Will reactivity apply to nested fields in objects and arrays?This is absolutely true. I have been confused many times figuring out what are reactive states and what are not.
I never knew Svelte needs changes like this, but seeing this, it sounds like a good plan.
1. Is there no way for the compiler to automatically, recursively find reactive dependencies?
2. Assuming no, is there not a more terse way to decorate reactive expressions?
It's just even less obvious to me why I might pick it over Vue at this point.
You can still use `onMount`. It's not deprecated [2], although `$effect` could be used similarly going forward.
[1] https://svelte-5-preview.vercel.app/docs/functions#untrack
[2] https://svelte-5-preview.vercel.app/docs/runes#$effect-what-...
let spaghetti = $state(uglyMess)
Does `uglyMess.thingOne[1].anotherThing = { moreMess }` re-render the whole tree or just the children of `uglyMess.thingOne[1].anotherThing`?See this for a real example: https://svelte-5-preview.vercel.app/docs/fine-grained-reacti...
Here is the same thing in SolidJS with more explanation: https://www.solidjs.com/tutorial/stores_nested_reactivity
Or have I missed something important?
They are too close for the similarities to be a mere coincidence
Or am I missing something important?
I've spent a lot of time building an intuition for how Javascript code executes and how I can combine its primitives to make reasonable abstractions. The fact that this _looks_ like Javascript but suddenly operates differently is almost more confusing to me than if Svelte was just a non-Javascript language.
React's `useState`, `useMemo`, etc. are perhaps more verbose but they're just functions. Dependency arrays are messy but it's fairly easy to extrapolate their behavior from a basic description.
Shared-everything does not scale. It ends in whack-a-mole and an escalating blame game about whether team members are fit to work on the project. It’s bunk. It’s deflecting from the real problem which was dereliction of duty by the “architects” who took a laissez-faire stance on state management instead of providing structure. Worked on a couple of those. Never again.
Practically the whole point of encapsulation is gated access to data, putting constraints on sharing, compressing the possible state space of the system closer to the desired state space.
They do an excellent job of some pretty substantial tech. But also the way he/they explain it is so powerful.
That includes the blog posts, the videos, the framework itself and the playground.
This does seem like a unifying step. Seems like (at least) Svelte and Angular have both declared this model superior to their existing implementations. The video gave credit to prior art in general but would have been better to give the specific projects credit - not just ethically but also to be explicit about what is and is not similar.
Facebook itself (and FB messenger) are pretty buggy apps that are built on React, so not even the "masters" know how to do React properly, judging by the results... To be honest, I don't know that the whole thing that KnockoutJS started is really necessary. These days I prefer to work with the DOM directly when possible. http://domenlightenment.com/ is a good resource for people wanting to explore how to implement HTML5 applications, going back to the basics.
GG
Crazy theory: esrap will be part of a transpiler that converts Svelte 4 code to Svelte 5 code. I'm not sure if that's actually the case or if it's even technically possible. But it would be really cool!
<script>
let thing_a = createThingA()
let thing_b = createThingB()
</script>
There's no hints from the Svelte language. There's no hints from the tooling. You have to manually open up both of those functions to see what they're doing. For a large code base, they probably just call another layer of utility functions so you that's another level to dig deeper.And that's on top of plain *.js/*.ts suddenly being hi-jacked. New team members can look at the *.svelte extension and know to go look at Svelte documentation. Why would they think to do that for plain *.js? They already know JavaScript.
https://github.com/brownplt/flapjax
https://cs.brown.edu/~sk/Publications/Papers/Published/mgbcg...
http://static.cs.brown.edu/research/pubs/theses/ugrad/2007/l...
It might be interesting to do a compare/contrast with v5's Runes and Flapjax's implementations.
And we are back full-circle ;)
The only drawback with MobX is that’s its upadates are as granular as components but in practice it’s not hard to optimise manually.
Is there any slam-dunk case for using Svelte over Vue?
- simpler compiler implementation - easier to identify moving parts
If so then the intro article needs a rewrite to be simpler without unnecessary districting details.
Anyone know what's going on here?
I never had so much fun and understanding of a UI framework as I did when working with KnowckoutJS and DurandalJS way back when.
If you actually want something different and better look at Imba.
I do not like magic in code.
I see it a bit like chess now, there will always be people making progress on a particular opening and variant.