I have recently joined a team that had invested in Svelte. I had previous experience in Angular and React. I was happy with React, but I decided to drink the koolaid and go all-in and do my best with Svelte.
6 months later, we're moving to React. It is partly because our Svelte code was messy enough to warrant a rewrite, and partly because we wanted to leverage the React ecosystem in that rewrite. It was also partly because I just don't like Svelte very much.
I think Svelte is clever, and interesting, and I'm glad it exists. But I found a few points insurmountable:
- Too much magic. The abstraction leaks and when it does, it requires a good mental model of Svelte's magic. I noticed this threw our juniors off.
- Special syntax for reactivity is not javascript
- I find the javascript-native flexibility of JSX far more expressive than any custom templating
- There are major tradeoffs made by doing things at compile time. Things that are easy in React at runtime turn out to be surprisingly difficult in Svelte
- Two-way data binding is a constant source of bugs
- Svelte practically requires let-style variable rebinding, often at a file wide scope, which again caused our juniors in particular to write bad code
- The style system is too opinionated for me. I also find it necessary to break into the global scope for any minor restyling of a subcomponent (or worse, pass in the style as a prop)
- Built in reactive store is cool, but our developers all ended up using it for tight coupling and global variables
- The strict one component per file approach can be cumbersome. It's not possible to write a one-line function component or similar
- Much smaller ecosystem than React, requiring much more to be done in house (exacerbated by all of the above).
- The faster runtime speed in no way made up for any of the above. Bad Svelte code is still slow compared to good React code.
I've not used any Svelte significantly yet, so your points may stand.
I have, however, written React for years. If anything in Svelte is more confusing to juniors than how useEffect works, especially in conjunction with unstable references, I would be literally amazed.
useEffect is a loaded automatic pointed at your face if you don't fully understand it. It is truly a terrible API due to how intimately you have to understand JavaScript for it to always work properly.
I can't imagine anything worse in Svelte. If there is, I'd love to hear it.
I actually think that useEffect is an amazing abstraction. It lets you group related functionality like subscribing and unsubscribing into one place and makes it very difficult to leak stuff or use stale values.
I didn’t write many class components beforehand though, so my mental model didn’t have to change all that much.
I wrote a lot of class components and getting used to effects was a 2-week-long headache for me. But then it clicked and I can't go back ever again. Hooks are really nice abstraction. However, sometimes I would like people to name their effects: `useEffect(function clickHandler() {})`, but I understand it looks ugly.
I don't disagree that useEffect is a great extraction overall. But the mental load to understand it vs. explicit lifecycle methods is significantly worse.
The ability to subscribe is indeed amazing (this was much more verbose in class components).
Same experience, I've started working with React when Hooks were introduced in a greenfield project. My last experience with JavaScript was jQuery and Angular 1. I was blown away by the functional-declarative approach of React Hooks, I can't believe that people haven't used it before.
agreed. transition from classes to hooks and useEffect was confusing at first but it now seems very predictable and sensible (does get a little hairy when you have one effect dependent on another).
just started writing my own named effects (eg useAuth() for encapsulating firebase auth) and still in a bit of an uncanny valley with doing that - but again it's very logical once you get used to it and actually in the end a simpler mental model of component state.
never want to go back.
junior devs, start with useState, it'll all flow from there.
If you use `<script setup>` in Vue, yeah it's some compiler magic similar to Svelte, but otherwise that's not really the case, for example Vue tracks depdendencies using ES6 Proxy, which is just a language feature.
Nothing about its usage implies what it actually does. It's about API affordances. You can't simply just read the code as someone inexperienced with React and say "oh, I know what this does". Returning a closure as the result of another closure has no inherent meaning as to when that closure would be called, because there's no "name" for that functionality.
And don't even get me started about the dependencies array, which has different behaviour whether you omit the argument, or pass an empty array, or pass actual `useState` dependencies, or pass other unrelated variables. Again, it's something you just "need to memorize" to use React.
If I hadn't been using React for many years before they introduced useEffect and various other hooks, they would have all been incredibly confusing. They still are really confusing when used in "clever" ways, especially with developers being allergic to writing comments. Even something as simple as `// this will do X when the component unmounts` make reading usages of useEffect 10x easier (vs. having to remember what happens when you return a function)
Sometimes I do miss the simplicity of componentDidMount... I guess you can always still write class-based components. Too bad they're not in vogue anymore! Feels like every library is hooks-based these days.
Totally agree re comments. They're necessary. Hooks do have huge advantages for composability and flexibility, and that can't be denied. But the API is just so arcane.
If my company wasn't a React shop, I'd probably be using Vue. I used Vue at my previous company and liked it a lot.
Headless libraries like `react-table` that use hooks are a revelation though. So nice to get pretty much all the behaviour you could ever need, without it making UI decisions for you. Difficult to use at first and wrap your head around, but hoo boy is it great to have the power to fully control the rendering!
I agree it has. I think this is because hooks have been talked about by facebook as the future of react. There seems to be no appetite for class based react improvements in the future. No wonder people move to the thing that will be better supported, regardless what it is.
useEffect makes easy things easy and hard things very hard indeed.
After building a few non trivial apps in React with hooks I'm convinced that what you really need for that model to work smoothly is a new language that tracks things like hook dependencies for you.
The ESLint plugin helps. But it depends on static analysis. And static analysis is fundamentally incapable of taking advantage of any information only available (or more easily accessible) at runtime.
One limitation that pops up fairly frequently in my experience: Only React's first party hooks can get exceptional treatment by the plugin, i.e. the setState function returned from useState can be assumed to never change and omitted from dependency lists, because those cases are hard coded into the static analysis. There's no way for custom/third-party hooks to indicate to the plugin that the thing they return will never change in the same manner, even if all they're doing is passing through a setState function directly from a useState call that will very obviously also never change reference, for instance.
They can probably offer some rudimentary support for this use case with static analysis by offering some kind of exceptions API for the user to spell out the names of the hooks and return values that should get this preferential treatment, like they have with the additionalHooks config option [1], but the DX around integration with third party modules is going to remain a messy unsolved problem.
Language/runtime level support for this pattern would hopefully be flexible enough to know that the thing we returned from our custom hook or third-party hook is fundamentally the same thing as (or shares some runtime property with) the setState returned from a useState hook, and treat it the same way, regardless of what our hook is called or how it's implemented or where it's from.
Unfortunately it sounds like the React team is planning to double down on static analysis to improve hook DX, so this doesn't seem to be an area they'd be likely to invest in. I think that's a shame because static analysis is much more difficult for users to extend and experiment with new paradigms for than runtime abstractions, and will start to stifle innovation in the ecosystem as we come to rely on it for more and more heavy lifting.
[1] Which, by the way, stops working as soon as you deviate even a tiny bit from the assumptions made in the static analysis, in ways that would be completely trivial to infer as functionally identical for any runtime system. The only way to address these shortcomings is to send in a PR to make the static analysis make fewer assumptions (unlike a runtime system which you can usually extend through some clever wrapping if it's just a function), for which YMMV: https://github.com/facebook/react/pull/20051
I agree with all of this except your terminology: static analysis of javascript can't solve this problem because of the limitations of the language semantics. Static analysis in general could, I think. It just might require a different language with different semantics.
Perhaps a shift in mindset about how to approach using Svelte would help. Svelte is not React, and using React for an extended period really changes how you think about frontend code (structure-wise etc).
As a quick example, the Svelte 'ecosystem' is simply the Javascript ecosystem, which existed well before React. You can import any npm module to a Svelte app. Outside of the React-specific packages (which I argue are overkill), such as Redux or Redux-enabled form libraries, what are you missing? Pre-styled components like calendars, modals, popups, are available in a range of vanilla-JS packages. Anything more than that and it's possible that React thinking has over-complicated your approach.
I'm looking forward to picking up Svelte and just going back OOP for state management. Immutable state with React was a massive eye opener, but right now I think OOP is just better for state management, especially with all the insights gained from using React.
I am really curious to hear about specific examples of some of these points. Specifically:
> The abstraction leaks and when it does, it requires a good mental model of Svelte's magic.
Maybe because I have familiarized myself with how Svelte works I haven't noticed anything like that.
> Things that are easy in React at runtime turn out to be surprisingly difficult in Svelte
I suspect this is from trying to use React patterns in Svelte, which I've seen quite a lot in people asking questions on Reddit. But I'm sure there are some things like that, could you give an example?
> Two-way data binding is a constant source of bugs
Again, very curious to see an example of this.
> It's not possible to write a one-line function component or similar
What was your use case here? I haven't found myself wanting to do that anywhere.
Is it surprising that two-way data binding is a source of bugs? We learned that lesson from Angular, and it influenced React’s preference for one-way dataflow.
Any error-prone pattern can remain useful with enough discipline, but we've learned time and time again that relying solely on discipline to cover for error-prone patterns doesn't scale.
Define "we". :) I understand this sentiment though, and I also understand designing a flexible system with zero footguns is nigh impossible. My point however was that two-way databinding is not rare in React, and there are other potential footguns to be had in idiomatic React anyway (hooks, React Context).
I realized maybe we have different definitions of 2-way data-binding also haha...
My objection is towards the Angular 1 style patterns where by passing some local component state to some children, you also implicitly allow those children to update it by binding it to some prop or by mutating it directly. This means the moment you need to share some piece of state, you can no longer reason about the state of your component in isolation nor control the ways in which it can be updated. This is at the core of what makes this approach so error prone IME.
In React, state is passed from parent to children as read-only values for which direct modification has no effect (this is the "unidirectional data flow"). The only way for a child component to update the state of a parent component is for the parent to explicitly pass down a function that exposes that functionality as part of its explicit API contract with its children.
Even in the worst case where we just naively pass down state updater functions directly (this is the case that's most similar to Angular-style 2-way-binding, which might be what you're referring to), this still results in state updates that are far easier to trace and debug as you can simply follow the function reference down to the call sites (instead of having follow all use cases of the state you passed down and trying to figure out which use cases bind or mutate). In the best case, it allows for components to offer only the minimum possible API surface to children by further restricting the set of possible state transitions (i.e. passing down openModal, closeModal functions, instead of the setIsModalOpen state updater function directly), and you can look at the parent component in isolation and confidently assert that no other possible state updates can possibly occur.
FWIW I don't have much experience with Svelte, so can't speak for which approach it's more similar to. But when I hear 2-way data binding the Angular style is the first thing that pops up in my mind from all the PTSD, so if Svelte's approach is more similar to the React style then I have nothing to object to.
Not to argue semantics, but that pattern is still called two-way databinding in my neck of the woods. In this deprecated page from the official docs, two-way databinding is defined as:
> Two-way binding — implicitly enforcing that some value in the DOM is always consistent with some React state — is concise and supports a wide variety of applications.
It's not an abstraction leaking, it's an important point to understand about mutating objects.
You are allowed to use `const` to declare an object and then modify its properties, because modifying properties is not the same as reassigning.
You're absolutely right that it's something that might catch you out, but not understanding the difference between reassigning and mutating is likely to lead to other bugs and falling into other traps in future.
(by 'it's not an abstraction leaking', I mean 'the abstraction's behaviour is consistent with the behaviour of the language' - I guess it's a subjective opinion that it's not leaking)
Other reactive frameworks like MobX will react when you only mutate objects. APIs like the useState hook at least make it obvious that you need to replace the whole object. Svelte on the other hand presents this "it's cool man, just change your plain JS state like normal!" paradigm, and it would be very reasonable without being told otherwise to assume that this applies to mutating objects as well. But then it doesn't.
If re-assignment of the whole object is what I have to do anyway, I'd much rather just be given an API that's slightly clunkier but makes that fact obvious. Otherwise the behavior will be surprising and error-prone to those not closely familiar with it yet.
>Well, let's see about that. React components are driven off state, and setState calls are sort of like data events. And React's Hooks and JSX are basically declarative. So what's the issue here?
>Well actually very little. There is only one key difference, React decouples the data events from component updates. In the middle, it has a scheduler. You may setState a dozen times but React takes notice of which components have been scheduled to update and doesn't bother doing so until it is ready.
>But all of this is a type of buffering. Not only is the queue filled by the state update event, but the scheduling of processing that queue is as well. React isn't sitting there with some ever-present polling mechanism to poll for changes. The same events drive the whole system.
>So is React not reactive? Only if you view reactivity as a push-only mechanism. Sure React's scheduling generally doesn't play as nice with push-based reactive systems as some would want but that is hardly evidence. It seems to pass the general criteria. But it is definitely not typical reactivity. Know what else isn't? Svelte.
Using Svelte finally gets me back into the joyous productive groove that I used to enjoy so much when developing with OpenLaszlo, using prototypical "instance first" declarative constraint based programming (beyond what the kids call "reactive" these days), but with standard unbastardized JavaScript and HTML instead of Flash and XML.
There's a lot to learn from past systems that predated React and Svelte, including OpenLaszlo, Garnet and NeWS, which I've used and love to explain:
>OpenLaszlo is a discontinued open-source platform for the development and delivery of rich web applications. It is released under the Open Source Initiative certified Common Public License (CPL).
>Oliver Steele describes "Instance First Development", which the language he designed, OpenLaszlo, supported through the "Instance Substitution Principle". I've written about it here before, and here are some links and excerpts.
>In the right context, prototypes can enable Instance-First Development, which is a very powerful technique that allows you to quickly and iteratively develop working code, while delaying and avoiding abstraction until it's actually needed, when the abstraction requirements are better understood and informed from experience with working code.
>That approach results in fewer unnecessary and more useful abstractions, because they follow the contours and requirements of the actual working code, instead of trying to predict and dictate and over-engineer it before it even works.
>Instance-First Development works well for user interface programming, because so many buttons and widgets and control panels are one-off specialized objects, each with their own small snippets of special purpose code, methods, constraints, bindings and event handlers, so it's not necessary to make separate (and myriad) trivial classes for each one.
>Oliver Steele describes Instance-First Development as supported by OpenLaszlo here:
>LZX is a prototype-based language: any attribute that can be attached to a class definition, can be attached to an instance of that class instead. This is handy in UI programming, where there are a number of objects with one-off behaviors. It’s also handy in prototyping and incremental program development, where it creates the possibility for a novel kind of refactoring.
>[...] The mantle of constraint based programming (but not Instance First Development) has been recently taken up by "Reactive Programming" craze (which is great, but would be better with a more homoiconic language that supported Instance First Development and the Instance Substitution Principle, which are different but complementary features with a lot of synergy). The term "Reactive Programming" describes a popular old idea: what spreadsheets had been doing for decades. [...]
>Oliver Steele (one of the architects of OpenLaszlo, and a great Lisp programmer) describes how OpenLaszlo supports "instance first development" and "rethinking MVC":
>[...] I've used OpenLaszlo a lot, and I will testify that the "instance first" technique that Oliver describes is great fun, works very well, and it's perfect for the kind of exploratory / productizing programming I like to do. (Like tacking a sailboat against the wind, first exploring by creating instances, then refactoring into reusable building block classes, then exploring further with those...)
>OpenLaszlo's declarative syntax, prototype based object system, xml data binding and constraints support that directly and make it easy.
>OpenLaszlo's declarative syntax and compiler directly support instance first development (with a prototype based object system) and constraints (built on top of events and delegates -- the compiler parses the constraint expressions and automatically wires up dependences), in a way that is hard to express elegantly in less dynamic, reflective languages. (Of course it was straightforward for Garnet to do with Common Lisp macros!)
>OpenLaszlo is an open source platform for developing user friendly web based applications, which work identically across all popular browsers and platforms (Windows, Mac, Linux, IE, Firefox, Safari, etc). It's ideal for presenting and editing raw XML data generated by PHP and other web services.
>OpenLaszlo supports a rich graphics model with scalable vectors, bitmaps, movies, animation, transparency, fonts, audio, streaming media, reusable components, user interface widgets, control panels, property sheets, keyboard navigation, browser "back button" navigation, as well as advanced WYSIWYG text and graphical editing tools. In other words, OpenLaszlo is the velvet glove for the iron fist of PHP. What can OpenLaszlo do?
>Garnet is an advanced user interface development environment written in Common Lisp, developed by Brad Meyers (the author of the article). I worked for Brad on the Garnet project at the CMU CS department back in 1992-3.
>One thing I like about Brad Meyers is that he's a strong programmer, as well as an excellent researcher, so he had a first-hand understanding of the real-world issues involved in programming languages and user interface architecture, unlike many academics who talk a lot of theory but never get their hands dirty. Brad Meyers understands where the rubber hits the road, and how important it is to have good tires.
>At the time I worked on it, Garnet didn't have pretty graphics like Flash, but the underlying programming system had some advanced features that are sorely lacking from most modern user interface development environments.
>Laszlo is a modern open source GUI programming system, with many of Garnet's advanced "natural programming" features like prototypes and constraints. Laszlo currently uses Flash as its virtual machine, but it's a much higher level way to program dynamic interactive web based applications, without using the proprietary Flash authoring tool.
>Garnet had a true prototype based OOP system (somewhat like Self), which is great for gui programming, because guis have so many objects that look and behave like each other except for a few little customizations (like the layout, graphical style, data source and call-back behavior).
>Garnet also had an automatic constraint system, which enabled you to simply define any attribute as a formula that depend on other attributes, without needing to worry about how and when the values were calculated. Garnet's constraint system automatically figured out the dependences of each formula, and automatically and efficiently recalculated and cached any values that needed to be updated, but only when necessary.
>With constraints, you can make a button inside a window, and define its left edge to be ((parent.width - self.width) / 2), and it will automatically remain horizontally centered in the window from then on, without you (the programmer) having to worry about what to do when the parent window's size changes.
>Without constraints, you have to manually write all the code that changes the button position whenever the window size changes, which results in code scattered all over the place in different classes and handlers and intermediate objects.
>Constraints are much easier to use and more general purpose than resize handlers, springs and struts, complex MVC updating schemes, and other Rube Goldberg devices.
>Constraints are especially useful for user interface programming, because they save you from having to write lots of annoying boiler plate and error prone code for handling updates (registering, chasing down dependencies, detecting changes, notifying updates, all happens automatically).
>Constraints make GUI programming much easier, but they're also useful anywhere in your program where one value is defined in terms of other values that might change at any time.
>Once you've tasted a programming language with constraints, you will not want to go back. Programming without constraints is like writing in machine language: error prone, low level, tedious, inefficient and mind numbing.
>Constraints are like structured programming for variables: In the same way that it's better to use loops and conditionals instead of gotos, it's also better to use declarative programming that says what you mean, instead of imperative peeks and pokes and side effects.
>Constraints let you write easy to read code, and concentrate on the interesting high level stuff that matters. You can go back later and change the layout of a complex GUI, without rewriting lots of fragile layout and event hanling code. Look at any MFC program to see how bad it can get without constraints.
>Constraints are natural and close to the way you think, because they let you declare a variable and the formula that defines its value in one place, instead of scattered all around the code. They off-load the tedious task of tracking down and maintaining all the dependencies from the programmer to the computer, which is much better at that kind of stuff.
>Garbage collection is like constraints: the computer can do a much better job than the human at performing the task perfectly, so spending some cpu time on automatic garbage collection and constraint maintenance is well worth the significant increase in programmer productivity and software reliability.
>Garnet had a prototype based object system. It was implemented in Lisp with a system called KR (Knowledge Representation, classic AI "frames" with slots and inheritance). KR was extended with an automatic constraint system that parsed the formula expressions (written in Lisp macros), figured out the dependences, wired up and maintained the dependency graph.
>An expression like "((parent.width - self.width) / 2)" would depend on self's width slot, self's parent slot, and parent's width slot. If any of them changed, then that formula would be automatically invalidated, and only recalculated on demand when it (or something that depended on it) is required.
>The cool thing was that you can make a prototype object, like a button, which has sub-structures like a label, border, drop shadow, etc. The sub-structures can be constrained to the button's dimensions, the label is centered in the border, and the drop shadow floats below and to the right, so the button's layout is automatically updated when it moves or resizes.
>The text color and border fill can depend on the button's "hilight" variable, so they automatically switch between bright and dark when you press the button (the input handler just toggles the "highlight" variable, and the graphics that depend on it are automatically updated).
>Now that you've composed and constrained a button to look and feel how you want, you can use it as a prototype to make other similar customizable buttons instances. Each instance can itself override the prototype's graphical properties, label text, action, etc.
>Instances of a prototype all magically inherit (instances of) the sub-structure of the prototype! It all just works the way you'd expect it to (with a lot of plumbing going on automatically behind the scenes). There's no need to make a separate class for each different style of button or action - prototypes let you customize any instance itself!
>Like Garnet, Laszlo is an advanced open source user interface development environment that supports prototype based OOP with constraints.
>Unlike Garnet, Laszlo deeply integrates recent trendy technologies like XML, JavaScript, Flash, data binding, networking, XML/RPC, SOAP, ReST, Java and Tomcat.
>Laszlo has a class/prototype based object system, and it (currently) uses the JavaScript runtime in Flash as its virtual machine. But it's more than just another way to program Flash.
>Unlike raw Flash, The Laszlo language is easy to learn and Laszlo programs are easy to read and write thanks to prototypes, constraints, declarative programming, and instance-first development.
>AJAX is a new buzzword for old (but not bad) ideas.
>Don't take this as anti-AJAX. That kind of architecture is great, but it's the notion that the new AJAX buzzword describes new ideas that annoys me.
>Of course Microsoft has been supporting it since the 90's, but it goes back a lot further than that.
>For a long time, I've been evangelizing and more importantly implementing interactive applications that run efficiently over thin wire (dial-up modems, ISDN, early internet before it was fast, etc), which are locally interactive and efficient because there's a programming language on each side of the connection that implements custom application specific protocols and provides immediate feedback without requiring network round trips.
>Before he made Java, James Gosling wrote the NeWS Window System. [...]
>NeWS (Network extensible Window System) is a discontinued windowing system developed by Sun Microsystems in the mid-1980s. Originally known as "SunDew", its primary authors were James Gosling and David S. H. Rosenthal. The NeWS interpreter was based on PostScript (as was the later Display PostScript, although the two projects were otherwise unrelated) extending it to allow interaction and multiple "contexts" to support windows. Like PostScript, NeWS could be used as a complete programming language, but unlike PostScript, NeWS could be used to make complete interactive programs with mouse support and a GUI.
HN discussion of "Sun's NeWS was a mistake, as are all toolkit-in-server windowing systems (2013) (utoronto.ca)":
>agumonkey>I've always been curious about NeWS but the web is quite short of demos about it (not helped by newspapers called Sun either). Do you know sites with videos about it ?
>Thanks for asking! ;) I've put up some old demos on youtube, and made illustrated transcriptions of some, and written some papers and articles. Sorry the compression is so terrible on some of the videos. Here are some links:
This is unfortunate for your team, but most of your conclusions are not adequate.
1. Actually, Svelte has much less "magic" involved than React. Sveltes abstractions are not leaky. The reactivity might be more difficult to understand. But the rules are well defined. They are strange - no doubt - but not leaky. When looking at the generated code it's actually much more obvious what is going on than what React is doing. React on the other hand has some leaky abstractions. The biggest one is the effort you have to put into to make it efficient (memoization). Also, hooks.
2. Two way data binding is not a constant source of bugs. Then every team using Vue, Svelte, Angular, React with MobX would have those troubles. I've done 8 years frontend dev in a large company using React and Angular (and recently Vue 3) with different teams. The bugs come from people not technology. I've seen no other correlation there.
Granted, React has the largest ecosystem of all of them, but what do you actually need?. In that regard, Vue hits the sweet spot.
> Two way data binding is not a constant source of bugs
Every team using AngularJS _did_ have those problems (not to say it didn’t have some niceties also). It was one of the big architectural changes in Angular (2+) as a direct result. React does not have this feature; passing down a setter function is not the same as automatic 2-way binding.
As to your first point, different things feel like magic to different people, so it’s really hard to debate this with other people with different perspectives unless maybe your talking about single specific instances. It’s like trying to tell someone they don’t feel cold because you don’t feel cold or vice versa.
Sure, but it maps 100% to an expected JS syntax and improves readability (subjective).
From
<div attr1="str">{children}</div>
To
React.createElement("div", { attr1: "str" }, children)
Custom templating is not like that.
Subjective indeed. If the goal is to leverage the full expressiveness of JavaScript, you could just alias React.createElement to something like `h`. Or use something like https://github.com/caderek/react-plain
Yeah, but that's harder in practice. You often copy html snippets into your code, for example from tailwind or bootstrap samples. This is easier in JSX syntax; you'd just need to copy and fix up some attribute names.
> You often copy html snippets into your code, for example from tailwind or bootstrap samples. This is easier in JSX syntax; you'd just need to copy and fix up some attribute names.
I literally never do this, but I suppose there are people who use React/JSX as a glorified templating library. Even then I probably wouldn't do this and rather use a proper templating library where HTML is valid.
JSX is more akin to a macro than a DSL, which is why I strongly prefer it. Debugging errors in templating languages is invariably more frustrating and time consuming.
More on point, being constrained within the special language is miles away from the constraint of JSX, which is essentially limited to only allowing expressions, because the macro expands to expressions. Everything else is fair game, and that makes it far more expressive.
> Built in reactive store is cool, but our developers all ended up using it for tight coupling and global variables
I see this in every project where juniors or just undisciplined developers get to run without oversight. There's nothing special about Svelte stores here. Any store has this trap. There's no framework or library solution for good discipline about if, where, and how you share data.
> - Special syntax for reactivity is not javascript
Well, JSX syntax isn't JS either. And hooks semantics is not JS.
Learning new syntax is easy but we should consider how much the new semantics cause mistakes and to what extent they are incompatible with tools such as linters.
As I see it, the main issue with new syntaxes is the fact that they break compatibility with existing tooling. JSX is not fundamentally different in this regard, except for the fact that it gained so much mindshare that it brute-forced its way to getting first-class support in editors, language servers, different frameworks, even TypeScript. I don't predict that very many new JS-adjacent syntaxes will get that treatment from the industry.
True, although Svelte's syntax is chosen cleverly in a way that is not incompatible with existing tooling. (Starting a statement with $: is syntactically legal JS and a no-op.)
Another question is how much of Vercel's resources would it take to contribute Svelte support into all the major tools.
It's not technically incompatible, but it's mostly parseable nonsense in the plain language. So it really only serves to make sure syntax highlighters still work, which is just one of many aspects of tooling
Considering JSX's current level of support involved cooperation from multiple FAAMNGs, I doubt Vercel could match it
Can you give an example of tooling that is not compatible with encountering the $: label? I can only think of a linter complaining about an unused label "$" or a redefined label "$".
Compare this e.g. with how React linting has to know which functions are actually (custom) hooks, and they cannot have any idea about the semantics of custom hooks.
I'm sure it will let you make that label, but it won't be able to make any sense of it. Maybe I was slightly wrong that only syntax highlighting will be helped by the "standard" syntax, but the point remains that none of the (existing) tooling can properly make sense of those parts of the code. That $ isn't really a label, it's a reaction, which your linter knows nothing about and can't help you with; at best it can "work around" it.
> I find the javascript-native flexibility of JSX far more expressive than any custom templating
JSX is the most significant contribution that React brought to front-end programming. Personally I don't always use React, but I use JSX quite often. It's intuitive in a way custom syntax (the dozens of) never will be.
I'm still appalled by the state of many things in JS tbh. I have the impression you have to perpetually move your codebase by adapting to your stack underlying changes, may it be the build system, the framework, tools or libraries that deprecate themselves quickly...
The reasons that made start Opa are still there: There should be an easy single language to program web apps and a clean coherent single stack to build and run them.
Today, I mostly code in Go and I love it (single stack, tests built in) despite the lack of generics.
On the front-end, it's another story. If I have time one day, I'll rebuild something in that space.
React has special syntax for reactivity. What is useEffect? Is it a native JS function?
I don't understand why JSX is still being used. Why should I a HTML developer change how I write HTML for a framework? And why doesn't it allow style tags?
Svelte lets me use HTML, style tags even a script tag. The only framework that is close to HTML is Svelte. The second version even had .html extension which is now .svelte.
You can 100% use style tags in React, they just aren’t automatically wrapped in module/component scope. Just like normal HTML style tags, actually.
As for JSX, I personally have always hated HTML template languages (“now which slightly-different version of the for iterator does this template get use?”) but I also recognize that there are other people who have opposite preferences. JSX-in-JS and template-directives-in-HTML are both valid approaches and it’s great that there are solutions to cater to various preferences. Trying to argue one down because you don’t like it is like arguing that your favorite color is factually superior.
> Built in reactive store is cool, but our developers all ended up using it for tight coupling and global variables
I don't see how having a reactive store has anything to do with the latter. But I find this ironic because every React codebase I've ever seen has components that import globals from other files (whether they be constants or POJOs). I don't find this inherently wrong, but it's exacerbated further by "Providers" and React.Context which seems idiomatic at this point.
Every reason you’ve stated are reasons I’ve switched from one proprietary or open source framework before react to react. 2 way data binding is a mental prison in big apps, I’m sure there’s a good implementation somewhere but I’ve yet to see it.
I don't like templating, it's too restrictive, feels like back to the 90s. This is typically aimed at coders who secretly don't want to code, and fall for tools that seems like they can avoid it.
What I like most about React is being able to break down views into functions and compose them. And if I have the choice, I prefer pure javascript and just a small helper function for Document.createElement instead of JSX, it makes it even more powerful, flexible and less verbose. You can already programmatically create elements in Javascript, I don't understand at all why people use "frameworks" for this, JS and HTML is already a view and a controller, just write the app? A complex single page application needs a couple of dozen lines of helper functions, that's all, why invent a whole new language on top of the language that in the end only restricts what you already have in the first place? This is spring framework enterprise hell all over again in the frontend
I've been developing websites and apps with Svelte for almost 2 years now, and my experience couldn't be more different from yours.
> - Too much magic. The abstraction leaks and when it does, it requires a good mental model of Svelte's magic. I noticed this threw our juniors off.
Since Svelte runs vanilla JS and works with the DOM directly, my experience debugging any "magic" has been extremely straightforward compared to other frameworks. I'm curious what specific issues you have encountered.
> - I find the javascript-native flexibility of JSX far more expressive than any custom templating
Not once have I wished I had JSX in a Svelte project, and the readability of Svelte's minimal templating sugar is a breath of fresh air for myself. What's an example of a limitation you've faced in practice?
> - There are major trade offs made by doing things at compile time. Things that are easy in React at runtime turn out to be surprisingly difficult in Svelte.
I would love an example of this, as I've yet to run into any situations where I felt there was something I couldn't accomplish at runtime.
> - Two-way data binding is a constant source of bugs
It's rare that I use two-way data binding, but in the rare cases where it's appropriate, it's very convenient. The improper use of a utility doesn't necessarily discredit the value of said utility.
> - Svelte practically requires let-style variable rebinding, often at a file wide scope, which again caused our juniors in particular to write bad code
I'm not sure this is true- with access to JS modules, stores, nullish coalescing, and lifecycle hooks, let-style rebinding can be avoided entirely.
> - Built in reactive store is cool, but our developers all ended up using it for tight coupling and global variables
Inexperienced developers will write bad code with or without powerful tools like Svelte stores. I don't think a framework is meant to replace experience, guidance, and coaching.
> - The style system is too opinionated for me. I also find it necessary to break into the global scope for any minor restyling of a subcomponent (or worse, pass in the style as a prop)
Considering you Svelte lets you style things in a plethora of different ways (including the traditional method of external css files), I'm curious how you came to the conclusion that it's opinionated.
> - The strict one component per file approach can be cumbersome. It's not possible to write a one-line function component or similar
While it _can_ be, a single function shouldn't be a "component"- but Svelte "actions" are an awesome feature designed to make single functions easy to compose and reuse across components. That being said, there is talk of supporting multiple Svelte components per file (although it's rather controversial and largely unnecessary in Svelte projects).
> - Much smaller ecosystem than React, requiring much more to be done in house (exacerbated by all of the above).
Considering any JS library (most importantly, ones that interact with the DOM) is plug-and-play in a Svelte file- I've never found myself needing something that didn't exist on NPM. Nonetheless, the DX and power of Svelte enables us to create complex libraries and components that would ordinarily be too cumbersome or time consuming to produce in-house, and with much less code than with React. This usually pays dividends in the long-run, as custom solutions are typically easier to debug and extend.
> - The faster runtime speed in no way made up for any of the above. Bad Svelte code is still slow compared to good React code.
The runtime speed is pretty low on my list of reasons I prefer Svelte above other solutions. While the performance gains from Svelte are amazing for users (especially ones with low-end devices or shotty internet speeds), the unparalleled DX, development speed, maintainability (do more with less code = less bugs = easier to maintain), community, and overall fun involved in working with Svelte overshadow the free optimization done by the compiler at build-time.
Anyways, I hope that round 2 will be more fruitful for you and your team! I'm highly skeptical that React will solve more problems than it causes when your primary bottleneck is developer experience, but I'm sure we will both be able to learn from your experience regardless of the outcome!
> Inexperienced developers will write bad code with or without powerful tools like Svelte stores. I don't think a framework is meant to replace experience, guidance, and coaching.
This is the gist of the whole comment. The person posting is not complaining about a bad Svelte experience, they're complaining about bad programming practices.
>Any top-level statement (i.e. not inside a block or a function) can be made reactive by prefixing it with the $: JS label syntax. Reactive statements run immediately before the component updates, whenever the values that they depend on have changed.
>In the long list of cool features of svelte, The one which I use more often is this dollar label thing. Initially, it looked strange to me but later I realized it is a valid javascript syntax. You can use it for doing side effects like useEffect of React, there is also a syntactic sugar way of declaring computed properties.
The Svelte compiler does add special SEMANTICS to JavaScript (and that's what makes it so great), but it does NOT add any special SYNTAX at all.
Although it might mistakenly appear to you that it does, if you're not entirely familiar with the well documented standard JavaScript syntax definition that I linked to above.
Your off-the-shelf code highlighters and linters will work just fine inside the standard <script> tags in svelte files, exactly like they should. That was the whole point behind Svelte's design.
However that's not the case with JSX, because JSX is definitely NOT the same as JavaScript, no matter how fuzzy and inconsistent a definition of "equality" and how many equal signs you use.
Much of this I would have expected, but this was less obvious:
> There are major tradeoffs made by doing things at compile time. Things that are easy in React at runtime turn out to be surprisingly difficult in Svelte
I read it as: Svelte is faster in theory, but since people write bad Svelte code, it is slower than the React code the same developers would write.
My interpretation is that this can be attributed to either svelte being hard to write, or people being used to React. Both of which are a challenge to companies looking to switch to Svelte.
6 months later, we're moving to React. It is partly because our Svelte code was messy enough to warrant a rewrite, and partly because we wanted to leverage the React ecosystem in that rewrite. It was also partly because I just don't like Svelte very much.
I think Svelte is clever, and interesting, and I'm glad it exists. But I found a few points insurmountable:
- Too much magic. The abstraction leaks and when it does, it requires a good mental model of Svelte's magic. I noticed this threw our juniors off.
- Special syntax for reactivity is not javascript
- I find the javascript-native flexibility of JSX far more expressive than any custom templating
- There are major tradeoffs made by doing things at compile time. Things that are easy in React at runtime turn out to be surprisingly difficult in Svelte
- Two-way data binding is a constant source of bugs
- Svelte practically requires let-style variable rebinding, often at a file wide scope, which again caused our juniors in particular to write bad code
- The style system is too opinionated for me. I also find it necessary to break into the global scope for any minor restyling of a subcomponent (or worse, pass in the style as a prop)
- Built in reactive store is cool, but our developers all ended up using it for tight coupling and global variables
- The strict one component per file approach can be cumbersome. It's not possible to write a one-line function component or similar
- Much smaller ecosystem than React, requiring much more to be done in house (exacerbated by all of the above).
- The faster runtime speed in no way made up for any of the above. Bad Svelte code is still slow compared to good React code.