Hacker News new | past | comments | ask | show | jobs | submit login
What dif­fer­enti­ates front-end frame­works (themer.dev)
302 points by thunderbong on July 19, 2023 | hide | past | favorite | 241 comments



> To overcome the shortcomings of the default change detection paradigm, the Angular team is working on a new approach called Signals. Conceptually, signals are similar to Svelte stores (which we’ll get to later), and fundamentally, they solve the change detection problem the same way as React; the framework is taking control over the application’s state so that changes can be easily monitored and re-renders can be as efficient as possible.

I radically disagree with this statement. Angular Signals (and signals in general, as in SolidJS) could not be more different from React's change detection model, and they are not particularly similar to Svelte stores either.

In React, your component function is a render function, which reruns. React diffs the state object when `setState` is called, determining which part of the DOM to update.

Signals-based solutions are generally much more fine-grained: the framework knows which portion of the DOM was affected by the signal, and does not need to diff the entire state object. This "surgical" approach requires more explicit state management on the part of the developer, through the use of Signals to wrap the application state. However, DOM nodes can be updated without doing a large, expensive diff. (As a corollary, the component function is typically a create function, not a render function.)

The developer experience is quite different as a result.


Signals have their own shortcomings as well. Once you leave the "re-run the render function on state change" you suddenly are dealing with implicit dependencies, stale closures, cyclical updating, and all sorts of weirdness in debugging things, figuring out what triggered what, etc.

Signals as a concept existed well before React and I think part of the genius of React is actually not using them. In a way a signal is just a very simple version of RxJS that Angular is moving away from, with a lot of the same downsides, just without all the pipe/abstraction mess.


> I think part of the genius of React is actually not using [signals]

Except that with the introduction of hooks (aka "OCaml 5.0 effects, but in JS, as a UI framework") you get some of the same interesting issues cropping up in React codebases as well, just spelled differently (stale closures, implicit dependencies, weird debugging issues)


Well you get explicit dependencies which have ups and downs, but closures do become an issue. That said, I find hooks less magical and less prone to cycles, especially as you're staying with a top down data flow generally.


For a little while it looked like mobx was going to displace Redux in React-world, and if that had happened it would have brought this to React without having to change React itself.


Mobx had one thing going for it -- it let you update your neighbor easily. But now Reacts useExternalStore achieves that cleanly with about 100 lines to wrap it in a cute API.

The proxy stuff I don't trust. Burned me once, never again.


The proxy stuff always works great when fiddling around with it in a small project. However, my experience has been the same. Whenever a project using proxies leaves the playground phase, all sorts of nasty bugs and performance issues arise.


> Signals-based solutions are generally much more fine-grained: the framework knows which portion of the DOM was affected by the signal, and does not need to diff the entire state object

I believe this to be generally pointless. Diffs are not that expensive, DOM changes are. The only case where this would help is you have a very large diff but a small amount of actual changes. React offers tools to handle this without changing the entire state management paradigm.


> React diffs the state object when `setState` is called, determining which part of the DOM to update.

Er, what? That's not true at all, even for class-based components. `setState` merely queues up another render of the function and its children. There is no diffing, except for the new DOM returned against what is currently rendered.


> and they are not particularly similar to Svelte stores either

How does Svelte differ? I’m somewhat familiar with SolidJS, and had always assumed Svelte was somewhat similar.


I'm not super familiar with svelte, but my understanding was that it solved it at compile time rather than run time.


There are a large number of very incorrect statements in this article.

> True to its de-facto tagline, change detection in React is “just JavaScript.”

Then in the example, the author updates a variable with `setCount(count - 1)`. That's not "just JavaScript". Just JavaScript would be `count = count + 1`.


Well, JavaScript has no simple way to react to an assignment like count = count + 1 without introducing some magic between the code you write and the final code that runs in the browser. Svelte is only able to achieve that experience because it has a compilation step, but React does not; hence why setState() is necessary and why people argue it's more "just JavaScript" because the code you write is pretty much the final code that runs (aside from the transpilation necessary to convert JSX syntax into JS, but this doesn't alter the way that anything works).

This isn't to make any kind of judgement on either approach though, I personally really enjoy the DX that Svelte offers.


Totally disagree with this.

Change detection is nothing but a hack to get around the fact that interacting directly with the browser DOM is very slow and blinky.

Imagine a world where interacting directly with the browser DOM didn't suck, then none of these libraries would exist.

The crux of the problem is that the browser immediately reflects changes to the DOM to the screen. And when you make a bunch of changes to the DOM you immediately see a bunch of changes happen, with portions of the screen being blanked out, and layout changes happening, and a whole bunch of other nasty stuff happening immediately, which all sucks. And updating a physical screen is slow too, double sucky. Change detection is a technique for minimizing updates to the DOM to avoid the suckiness.

I implemented word processors, and I've used a technique called 'double buffering' to rerender complete pages off-screen, fast. And then update the screen smoothly. If we used a proper word processor engine to display our UIs instead of a browser engine then we'd have no need for React etc... After decades, Chrome finally has a rational equivalent to this technique called the View Transitions API.

My hope is that the days of doing change detection are numbered and we can finally leave all this change detection crap behind us.


> the fact that interacting directly with the browser DOM is very slow and blinky.

This is not really true, or at least hasn't been for many years.

Change detection's primary purpose is to let other code know when something has changed, and what changed. You could theoretically re-render all your DOM based on that information, and in many cases that works much better than people expect. But the DOM is stateful, and truly large trees of 1000s of nodes are too slow to update, so frameworks try to also do minimal DOM updates.

> The crux of the problem is that the browser immediately reflects changes to the DOM to the screen. And when you make a bunch of changes to the DOM you immediately see a bunch of changes happen, with portions of the screen being blanked out, and layout changes happening, and a whole bunch of other nasty stuff happening immediately, which all sucks

This is also not really true. The browser doesn't update the screen until an animation frame. Anything you do synchronously is not rendered to the screen until code yields to the event loop. Even many async things, done on the microtask queue, block the next animation frame giving us visually atomic updates.

Lit, for instance, uses this to get async, batching rendering without unintended partial renders of the screen.


I don't see it that way. The main need for change detection in my opinion is to remove the need to update parts of the DOM in an imperative manner. It is fine to do that for smaller projects but when a project gets large, it becomes difficult to reason about the myriad of changes happening without a system to handle that. I find any one of the examples in the linked post way more easy to reason about than manual DOM updates.


Having worked extensively with the major frameworks in multiple orgs, I have largely found that templates synced with state via change detection (like Angular) result in more readable/maintainable projects.

While I personally like React, it's pretty tricky to use in reality. I have found that its idiosyncrasies make it less resilient to suboptimal contributions from engineers that are still early in their careers.

Broadly, the projects I have entered using React tend to be pretty wild, messy and unreliable.

There is cross over though, Angular projects using Redux (ngrx) are horrific. The criticism of Angular for me is the lack of control you have over the compiler, dependency versions and testing strategy.

Svelte and Vue are both interesting, but they use custom file formats (.vue, .svelte) which can add an extra layer of unreliability to projects using TypeScript and testing frameworks.

The advantage of Angular is that it's an ergonomic abstraction that allows optimisations to occur at the engine level. It's more like an ORM, where React is more like writing SQL queries directly.


I’ve only ever touched React as a “so and so needs a hand on this bug” or “we want this feature added to legacy while the React team works on the new version”, so I was already going into a codebase that had long been abandoned in all but usage.

That said, it was a nightmare. I think we had state managed by vuex, and I just needed to add a new data point to the component and display it. But the data needed to be added to state…so I think I touched 6-7 files, adding mutators and updating definitions etc.

The experience put me off of React, though I do imagine a lot of that comes down to how the code is initially structured and maintained. It’s clear that whatever they were doing was more “make it work now” and it so much worrying about the future.


Have you tried the model view update pattern?

If you haven't I recommend having a look. If found it amazing for maintainability.

You have to be carful to structure everything well. If not you blow up the number of messages in one file. But once you get past that it's really amazing.


Yeah I have, personally I like it but the issue I have found was that I am not the only one who works on projects.

> You have to be carful to structure everything well.

This essentially sums it up. I can be careful, but it's impractical to review code to ensure it's also structured correctly when working on a team with lots of contributors with varying experience levels.

In my experience, It's also more difficult to teach MVU than it is to teach templating with automatic reactivity.


Sadly, I have to agree with everything that you've written there.

For simple use cases templating is much better as well. It's only in complex state management that I find MVU is worth it.


I agree with you that going back to the JQuery days of unmaintainable imperative code would not be a good thing.

But with JQuery I was able to create adaptive UIs by applying 'transforms' to the DOM, I really miss that. Think of the way that 'tag helpers' can apply cross-cutting transforms to Razor Page applications, I used to do things like that with JQuery.

React et al cuts me off from both the browser DOM and it's own virtual DOM, so this I can no longer do this. I have to do everything the React way. Developing with React is far less powerful than JQuery. I still find modern frameworks to be a kind of straight-jacket. I hope to have my transforms back in a rational way someday.


React discourages direct DOM access, but it’s still possible.

https://react.dev/learn/escape-hatches


It seems like you are a ripe candidate for Svelte !


Engineers developing rendering engines for browsers definitely know what double buffering is. However, the accumulation of peculiar layout rules, support for CSS, and many other things make things hard. One of the major design flaws of the DOM was allowing unrestricted mutability. If DOM mutations were transactional by default, it would offer better control over layout reflows.

Change detection remains a common UI issue, even in native UIs. While rerendering everything on each UI loop is possible, as complexity increases, optimizations involve detecting changes. Smalltalk introduced MVC to identify changes and update the UI. Java Swing and Qt also adopted similar mechanisms with different names (observers, signals, and slots). Apple's Cocoa employs a variant of MVC as well. Change detection has historically been a common problem even in Win APIs.


I don't know... even if the DOM was super fast, would it really be ergonomic to keep all your state in the DOM tree and only work with that?

I kinda doubt it.

So then you'd have to store some state in JS, and some in the DOM, and again you get a syncing problem, since you lost your single source of truth.

Or did I misunderstand your comment?


At peak jquery, I argued successfully for storing the metadata on DOM, because then you have a system of record, upon which you can build a straightforward source of truth. React does a ton of work to solve that architectural puzzle another way.

The thing I have run into over and over again is that when we try to pretend the system is Y when it is in fact X, inevitably the impedance mismatch results in 1) bugs that should be easy to reason about but are difficult to fix, and 2) a form of pulling the ladder up behind you.

If I want to continue to work on interesting things I have to be able to carve out chunks of my code to gift to #3 on the bus number list, so I have the bandwidth to double down on some other topic or expand into another.

I know which side my bread is buttered on. Many don’t. Which is why I have a more consistent supply of mentees than many of my peers.


Data comes from the server as JSON. Data comes from the user as DOM events and changes. There's always going to be an impedance mismatch.

The DOM API sucks, so React et al went all in with state-as-objects. IMO it was the right call.


I just don't think DOM->JSON->server->microservice->DB->microservice->server->JSON->DOM is sustainable long term. It's had a good run, but it's a big part of the reason I'm looking at Phoenix for a personal project. React is predicated upon solving the wrong problem.

Talk about impedance mismatches.


I'm not sure switching to DOM->JSON->server->microservice->DB->microservice->server->DOM will make much of a difference.


Complexity is usually quadratic or factorial. Only a tiny number of systems are logarithmic. 20% reduction in parts can often be a 40-70% reduction in cognitive load.

But the funny thing with fractions is that if go above a threshold and come back from the brink, the path back is a smaller number than the path out. So if you go from 8 to 10 steps that's 25% worse and if you undo that bad decision you are getting 20% better. So you want to be careful to compare sustainable to unsustainable and not the other way around, if you follow me.


> I argued successfully for storing the metadata on DOM

Who did you convince?


A team working on an AJAX webapp that typically was meant to comfortable manage around 1000 blocks of state at a time. Somewhere on the order of 6-10 pieces of data per block (Which we just changed as a unit of work because 1000 was already hard enough). And when you're dealing with 10^3 you need a couple of different ways to visualize the same data or you're getting nowhere, so we had one or two other views that dealt with different levels of detail.


>> Or did I misunderstand your comment?

Well, I can see now that while I expressed my dislike for change detection I didn't paint a good picture of why I think we'd be better off without it :-).

While I appreciate React's declarative programming style I dislike the fact that React (and all the change-detecting alternatives) force me to interact with the DOM through their APIs.

JQuery allowed me to do whatever the hell I wanted to the DOM, but the resulting screens updates were very ugly. And yes, JQuery resulted in ugly and unmaintainable code but that's not on JQuery, there are rational ways to apply transforms to a DOM.

I want to "do whatever the hell I want" to the DOM because I want to apply cross-cutting, adaptive transforms to my application, like I could with JQuery. And I think that we'd all be better off for it. In the transition to React-like frameworks we have lost that ability

The view transition API lets me start a transition, do whatever the hell I want to the DOM, and then update the browser smoothly.

So what I'm imagining is a component-oriented framework like React that doesn't lock me into a component-only API. A framework that, when rendering occurs, starts a transition, let's me do whatever the hell I want to the DOM, and then updates the browser smoothly without doing change detection.

And the reason I would like that is because, while React's declarative, component-oriented development approach is nice and all, it's also very restrictive.

Reacts prevents me from directly traversing and mutating the DOM, and it's own virtual DOM, because if it did then it can't efficiently do change detection.

If we replace change detection with transitions then we can "do whatever the hell we want" (hopefully not in an unmaintainable JQuery way) and still have smoothly updating displays. We'd have a framework with all the benefits of React and without the restrictions.

Anyway, that's my best shot at explaining myself :-).


I‘ve written a UI a few months ago with only lithtml and direct DOM manipulation and it worked pretty well.

I needed to do calculations and reading bounding boxes from the DOM initially and on resize. Basically a hack to get around CSS limitations.

It was crucial that this is fast enough to avoid flickering.

I wouldn’t have attempted this with react. I think it would still be possible with useEffect, but I tried the most lightweight and fast by default approach that was feasible.

Have a look at lithtml.


in the cljs/reagent world anytime you need to do direct dom stuff you just use a ref - that must me the same for react proper?


You can pretty much do anything with a combination of refs, events and useEffect. It’s often fiddly and cumbersome though.

Aside:

That’s why I‘ve been gravitating towards approaches that have much simpler execution models.

You can still do declarative and functional. But you have more control when needed, create _much_ smaller bundles, have fewer dependencies, better default performance, allocate less garbage and you can reason about your code in more fundamental ways.

In cljs this would mean to not use a react wrapper.

However I personally try to avoid using any deps for frontend, and that includes cljs. Any dep needs a justification IMO.

That justification is often not good enough, since I’m mostly writing „interactive documents with some fancy parts“.

If you’re writing something like nextjournal, roam, data rabbit or similar then it’s a different story.


So what are you disagreeing with? Are you disagreeing that change detection is what mostly diffentiates the front-end frameworks? If so, what is that important factor that differentiates them if not the mechanism that modifies when change is detected? Why do you have such a strong belief that this factor isn't it?


They’re disagreeing with change detection being the most important part of a frontend framework by suggesting that the browser API change dramatically so that we fix the important problem of… change detection in frontend frameworks. Yeah it’s not the most consistent.


Change detection has nothing to do with the speed of DOM updates, it's about directly encoding dataflow dependencies to avoid code duplication and redundant UI updates. You could paper over the lack of dataflow updates by just updating the whole screen every time, but you still need to preserve various state, like scroll positions, highlighted text, etc. which still means only some state changes and other state does not.


Redundant UI updates could be okay if done quickly and cheaply. Games refresh entire framebuffers 60 times a second, redrawing the entire world, and it works fine, because they do that efficiently, and flip entire pages so that you never see a half-draw.


Full refreshes aren't cheap though, games consume a lot of power.


DOM wasn't meant for real GUI's and trying to trick it into being one seems a fool's errand. It was meant for static documents. DOM is the wrong tool for the job.

Time for a new state-ful GUI markup standard so we don't have to rely on the whacky DOM to get expected and common GUI idioms.

Maybe build the "engine" on top of the Tk or Qt kits to avoid starting from scratch.


That's not nearly all there is to it. If the DOM was fast and you could control redraws, it would still be horrible.

A performant big pile of mud is still a big ball of mud.


"A performant pile of mud" is a description that fits many things that still stay afloat exactly because the performance allows them to. Consider C++ or x64 as examples.


For example, there's the way it's still impossible to pass data between native Web Components without stringifying it first.


This is an absolutely stunningly false and ignorant statement. I can't believe it's still being repeated.

Web components are objects and they have properties that can be set. The entire web components community - which includes the developers of apps like Photoshop, Reddit, Chrome, Firefox, and a lot more - passes properties down through trees of web components _all the time_.


I reallly love web-components. They allow to encapsulate logic, and be re-used and composed with others. It starts feeling like unix in the browser, to me.

Maybe a bit verbose, but here is an example https://jsfiddle.net/8kvucm9f/5/ in the context of the blog post (and with my preferences of doing things; there are just many different ways of "writing web components", and can adapt many use cases).

Also, by using their HTML attributes (id="", name=""), it is possible to make really nice features with CSS.

Finally, they can offer a clear API (just like the <img/> or <video/> or <input/> elements), to the users of these elements.

They can be inserted, to give "markdown" documents (or .org mode etc. if it is converted to HTML), JSX like features, by being "just embedded HTML text".

It is super powerful, maybe feels a bit scary at first, but all problems seems to get solvable with clean patterns.


There is a subtle grain of truth to it - the implied "declaratively" here. React has went south quite a bit, but the basic idea of JSX where HTML becomes a language with first class objects, functions and bindings is extremely compelling. It offers a solution to the problems of complexity that pure HTML cannot tackle while still staying somewhat true to its declarative nature and reaping most of those benefits (instead of fully switching to the imperative DOM paradigm).


Adding "declaratively" doesn't make it true either.

You can declaratively pass objects between web components in every web components library out there like Lit or Stencil.

Now, you may claim that that doesn't count because the declarative part is implemented in non-standard userland libraries, to which I would reply:

1) So what? React is a non-standard userland library. You can't pass data declaratively between React components without React. At least with web components there's a standard _interface_ for the components so that many different libraries can implement the declarative parts in a completely interoperable way.

2) Yes, you can't pass objects around in plain HTML because plain HTML doesn't have references to the objects in the first place. Even if you did add a way to set properties in HTML, what would you set them to?

2b) This is actually likely changing as the Template Instantiation proposal moves along and possibly gets the ability to make property bindings. This will be useful for declaring custom elements that accept properties. But the question remains: where do those properties originate from? Most likely JavaScript, because HTML doesn't have any.


> You can declaratively pass objects between web components in every web components library out there like Lit or Stencil.

They capitalized Web Components, I think they're talking about what browsers natively support and not a library or framework.


> You can declaratively pass objects between web components in every web components library out there like Lit or Stencil.

This makes it a non-standard library-specific method of passing things.

I have no idea what you're arguing against.

There's literally no way to pass objects as attributes to web components. Web components had a great opportunity to get rid of attributes/properties dichotomy, but they doubled down on them.

> At least with web components there's a standard _interface_ for the components so that many different libraries can implement the declarative parts in a completely interoperable way.

Of course there's nothing standardised, and nothing interoperable. Because if one web component exposes its API through attributes, and the other one through properties, and a third one through both, never the three shall meet. Without library-specific hacks.

There's a reason the "standardized" lit uses a custom DSL to distinguish between things:

  `<some-component 
     attribute="string only" 
     ?boolean=${boolean}
     .property=${any_js_variable}
     @event_name=${callback_function}
  >`
So much standardization.

Edit:

Stencil uses JSX (non-standard), so it doesn't care and probably sets properties directly, skipping attributes. For object props and HTML it says it explicitly that you cannot set them (of course): https://stenciljs.com/docs/properties#object-props

> But the question remains: where do those properties originate from? Most likely JavaScript, because HTML doesn't have any.

Indeed. That is another issue with anything web-component-related. Moar Javascript. As if we don't have enough. There are now dozens of more JS APIs existing just because of web components, and even more are incoming: https://w3c.github.io/webcomponents-cg/2022.html


> Of course there's nothing standardised, and nothing interoperable.

This is so very much not true. On what basis do you even claim this?

Web components are JavaScript objects that extend from HTMLElement. They all share a common interface of attributes, children, events and properties.

You can get a reference to any web components any set a property on it:

    document.querySelector('my-element').someProp = {};
This works on all web components. It's interoperable because there's no special React, or Angular, or Vue version of an API to set properties.

> There's literally no way to pass objects as attributes to web components

How do you even get a reference to an object in HTML to pass as an attribute, if you could?

And who cares? You can pass objects as properties to web components. Attributes are string-valued, why would you want to pass an object to them?


> This is so very much not true. On what basis do you even claim this?

I worte the basis, and you completely ignored it.

> And who cares?

Ah yes. The mantra of all web-component zealots. "Who cares".

How it started [1]: "we have too much Javascript, we rely on too much Javascript for all our functionality, empty body tag is the bane of web sites" etc.

How it's going: Who cares? Dozens of new JS APIs! More JS APIs coming every month. Why would you want to do anything with web components outside of Javascript? Declarative? Oh, in any of the Javascript libraries. Javascript Javascript Javascript Javascript Javascript.

> Attributes are string-valued, why would you want to pass an object to them?

That is not what anyone wrote. Literally not a single person.

Anyway, this is hopeless. Web Component zealots are absolutely deaf and blind to anything outside of their world where goals shift faster than grains of sand in a hurricane.

[1] The original pitch for Web Components: https://fronteers.nl/congres/2011/sessions/web-components-an...


You started by saying it's false, then proceeded to write something unrelated.

If you start with such a statement, it would be best to follow up with proof instead of something unrelated.


Not via html attributes. But it's never been a problem to pass data via JS properties:

   someWebComponent.data = { foo: "bar" }


I think your idea of speeding up the DOM is the right one, but the view transitions API has almost nothing to do with it. That’s more for whole page changes, and doesn’t solve the main issues which is that the DOM simply does way too much and is bloated to all hell, and that JS is single threaded.

I’d like to see a new mode introduced ala “use strict”. I know the big brains at the top hate this but we need a way to shake off some legacy especially in the DOM. A few changes to its semantics could dramatically speed it up. And then the second part would be a way to share events and DOM across workers so we can put React essentially off the main thread. These two changes together would make websites able to feel like native apps.


> DOM simply does way too much and is bloated to all hell,

It's my observation that React and similar frameworks really help to bloat the DOM.

Now someone will say it is how people misuse React and similar frameworks.

and then I will say if large parts of the population using a technology make the same misuse of it, even people who know better, it seems like the technology supports that misuse and is to blame somehow.


I don’t agree at all, I don’t see any extra DOM due to React across many apps nor any fundamental reason why it’d cause it.


DOM changes are only a part of the story. Another part is a component model that allows to build complex software from tiny and sometimes messy individual pieces.


You want to store your application state in a widget tree? This was the part about the old bit 'o jquery approach that drove me absolutely batshit. The logical state of your UI is strewn about in Dom objects and their listeners, closures etc. There is no one place you can go to see what state is relevant to this component.


With the exception of list comprehension, which is IMO not only the best feature of jquery but almost enough of a reason to use it on its own, browsers have copied most of the features at this point. Do you (or anyone else here) see an opportunity to copy features from React into the browser?

I know there have been earlier tricks to pop elements off the DOM, modify them heavily and then pop them back on. I wonder if it would be better to a) provide a way to mark a node and it’s children as suspended reflow, or b) provide a virtual view where you can do operations on off-DOM nodes including ones that typically trigger reflow. c) something else entirely


Change detection is one thing, and double buffering is another. Change detection is just projecting from state changes to UI changes. It doesn't try to solve this hypothetical "flashing". That is entirely up to the browser. Double buffering is a graphic technique meant to show smooth UI changes at the cost of doubling the memory used for the graphical object.


> I implemented word processors, and I've used a technique called 'double buffering' to rerender complete pages off-screen, fast.

Node trees can be created without attaching them to a document. Then the whole tree can be attached with one call,

There’s no need to attach nodes individually. I don’t know why anyone would want to do that.


> I've used a technique called 'double buffering' to rerender complete pages off-screen, fast.

I'm absolutely not a web dev (as this comment will make clear), but I'm very surprised that browsers haven't been doing this all along. I just assumed that they did.


I'm sure they do, theres even a construct to do this called OffscreenCanvas


Any changes to the DOM aren't reflected on screen until your code stops running. If you see a partial update it's because your code is "await"ing something.


> then none of these libraries would exist.

I'd still have to use a framework for componentizing my components, scoping CSS, a namespaced store, etc


In all cases you're going to store and manage your state some place. What you describe is managing the state using the DOM itself, which would indeed be easier if the DOM wasn't directly tied to the screen, but which would still be very cumbersome because of the lack of appropriate APIs for managing the DOM as an application state.

If you were to go in this direction, you would eventually make the DOM less DOM-ish, and the painting mechanism more DOM-ish, effectively shifting everything one place and going full circle back to the frameworks.


Back in the VB6 days there was an option one had to toggle to enable the double buffer.

Without it all app paints had a noticeable lag.


We need DOM transactions.


I don't agree, because in my experience every framework is fast enough unless you're either past what it's capable of (you probably aren't) or you're using it wrong (you probably are). It is far more important to fine a framework that you like and that you understand, because then you'll write good code that the framework can run fast.


Some frameworks have nastier non-obvious footguns that make performance issues difficult to spot.

Funny example, in Blazor, if you have a mouse move event, by default it will decide that your entire component including children needs to be rerendered whenever the mouse moves by a single pixel. Unless you change state management to manual or whatever.


I agree. Of course there is no 'single factor' for almost anything.

So, going with the themes of attention seeking hyperbole, I would say the 'single biggest factor', is the ability to find and keep skilled developers in any of these top 10 frameworks. The differences mean nothing when the bigger picture is considered.


What are you disagreeing with?


Not a frontend person, and unlikely to become one anytime soon, but maybe someone can shed some light into the downsides of Svelte? The article fails to mention any (it's apparently "win-win"). Presumably if Svelte were the be-all-end-all of front-end frameworks, it would dominate soon enough?


React:

- had a 3 year head start on Svetle (2013 vs. 2016)

- has major engineering orgs behind it. Facebook started it. Microsoft makes it work with Typescript.

- is easier to get started with. While JS-compilation is common, it's required for Svetle.

Svelte isn't free of pain points. Like any form of magic, it has edge cases. It's better but not "better enough" to de-throne React.


> maybe someone can shed some light into the downsides of Svelte

Svelte trades off runtime size for component size. It was created in the context of infographics for the New York Times online and for projects that roughly line up with that it's pretty much the technically best option.

I like the Svelte authoring experience and introduced it for a few components in a React based low-code platform. The reason I phased it out was a chat component that was ~600 LoC and 50-something reactive variables in a moderately complex chain blew up into ~5k LoC of output. I also ran into what seemed to be some transient invalidation issues. I was short on time to debug this and engage with the Svelte community so I rewrote it in React to match the rest of the system. It's possible I was doing something wrong but I don't have enough confidence to bet on 3.x again. I'll take another look when 4.x comes around.

> Presumably if Svelte were the be-all-end-all of front-end frameworks, it would dominate soon enough?

There's significant network effects around the established frameworks. Nobody gets fired for picking React. I personally think Solid is the best overall technically but the ecosystem and mindshare is smaller and that matters to a company making a business and not necessarily technical decision.


> I also ran into what seemed to be some transient invalidation issues.

It sounds to me like you didn’t understand the details of Svelte’s reactivity model, which are very important, and fairly straightforward to learn, but different from what people are commonly used to—and so even if you theoretically learn the model, it may take time in more complex cases for it to come naturally. The crux of these sorts of problems tends to be that reactivity is a property of bindings, not data; and as a secondary effect, mutation is therefore rather hazardous.

> 50-something reactive variables in a moderately complex chain

It sounds also like you’re fighting the framework. I’ve seen stuff like what you describe in some React libraries (e.g. mirroring something DOMmy in React—common, but I hate it because it’s so much mindless duplication, among other faults), but in Svelte at this scale you’re likely to get better results from seeing if you can bag things together (e.g. let x = {a, b, c} rather than let a, b, c), or passing through components rather than reactive variables. But these are vague concepts which may not actually be relevant or applicable.

Svelte is a framework that definitely requires that you work with it, or you’ll have a miserable time, whereas with React you can, to a much greater extent, do awful things and get away with it.

And when you speak of 3.x and 4.x and such—there’s basically no chance any of this stuff will ever change in Svelte, it’s too fundamental. In some cases you might end up with better compiler warnings, but that’ll likely be the extent of it.


> It sounds to me like you didn’t understand the details of Svelte’s reactivity model

This is a reasonable assumption given what I've described. It's been almost two years but from what I remember the problem was updating what should have been the same reactive binding in two different locations. When trying to debug a problem I was stepping through the code and noticed that the indicies passed to `$$invalidate` were different. Checked for typos and shadowed declarations and found none. Rolled back to checkout and found the indicies matched. In the process of undoing/redoing my conclusion was that the introduction of an unrelated reactive binding was the difference. I could have been wrong; I was pretty exhausted at that point.

> It sounds also like you’re fighting the framework.

Sure. In this case it was getting a big chunk of JSON back from an endpoint I didn't control and trying to use a number of reactive variables to select out pieces for display. I've done similar things in a half dozen other reactive/FRP libraries without issue but I assumed I was off the beaten path in Svelte.

I realize that this is empty complaining about other peoples' hard work. I don't have the code any more and I don't have a reproducible test case so I have no problem with anybody waving this off.


Why do you care about compilation output?

Also, your component sounds cumbersome.


I’m mainly a C++ developer, not a frontend developer (though I dabble), and when I have a choice of multiple approaches the first thing I do is go to godbolt.org, implement MWEs and compare the assembly.

I also do a lot of code generation, and my absolute goal is to have it write code that I’d write if I were doing it by hand. That’s pretty key for me. If the output is better, then good. If the output is doing a bunch of stuff it doesn’t need to be doing because it’s taking my specific use case and making it conform with its own model for doing generic things, I don’t like it.

I was a web developer back when tables for layout, HTML attribute soup for styling, and applets for interactivity were going out of fashion. Semantic HTML and CSS for layout and styling and JS for interactivity were coming into fashion. Divs for layout, class soup for styling, and JS frameworks for interactivity hadn’t yet become mainstream when I stopped.

So my default is lean HTML, lean CSS, raw JS. Maybe JQuery if needs be. It’s an outdated default, but it’s mine. The thing is, I get great load times and performance for what I need. What I need is normal dashboard stuff - though because of the nature of the field I work in, it condenses a lot of information onto a page and that information is updating many times per second.

I don’t seem to get enough performance from the frontend frameworks I’ve tried. I don’t know if it comes from code bloat, or deep call stacks through god knows how many levels of indirection, the way DOM updates are issued, or something else - but it has just never seemed worth the learning curve to end up with something I can’t do myself with admittedly a lot of work.

As such, I find this review helpful. Svelte has caught my attention and I want to give it a go, but if the output is bloaty then that’s a red flag for me. If it’s bloaty because it isn’t doing backflips through the call stack, but is inlining code - that’s familiar territory and certainly something I can deal with.


> So my default is lean HTML, lean CSS, raw JS.

This is fine but having spent a decade of my career doing this, I would never advocate for it in an app with any kind of frontend complexity or a chance to grow into it. The frameworks mostly obviate the need to understand layout thrashing, resource cleanup, and retained state conflicts in the DOM. If you're doing simple stuff these don't matter but they're challenging to fix and more challenging to stay fixed when a group is contributing.

> Svelte has caught my attention and I want to give it a go, but if the output is bloaty then that’s a red flag for me

Your use case is a pretty good fit for Svelte and you're likely to get output you'd find acceptable and given your preferences I think you're more likely to prefer its sensibilities over other frameworks that could provide the perf you're looking for. The tradeoff is kind of like the tradeoff between monomorphization vs virtual dispatch. The Svelte compiler is basically inlining the update code instead of using shared code in a runtime.


FWIW it's a strange anecdote to me, as I remember the opposite being the case - svelte app being significantly smaller than React.

Svelte's whole shtick is using ASTs from the compiler to stuff at compile time where possible, to reduce bundle size and complexity.


> the way DOM updates are issued

This one, id wager. There isn’t necessarily anything wrong with your approach but Id agree that you should try svelte for this reason.


Because the article makes it sound like your application code will be smaller because you don't need to ship the change detection library/framework. But in a big enough application, the explicit change detection code added by the compiler adds up to more lines of code than most frameworks.


> Why do you care about compilation output?

I was advocating/piloting a new framework in the app and was unhappy with the output versus the input and perceived complexity. I spend a significant fraction of my time cleaning up performance issues. A good chunk of that is download size so I care in general about what I'm sending across the wire.

> Also, your component sounds cumbersome.

Sure. It was the classic feature creep scenario.


I'm disappointed that this isn't talked about, even when someone explicitly asks: You can't use JS to iterate over, store or otherwise work with the output of the templating language.

For example:

    const cells: Record<string, ReactNode> = { a: "hello world", b: <>hello <b>world</b></>, c: <Icon src="world.png" />}
The closest you could get in Svelte, is creating three new components (3 entire files), which are then rendered using `<svelte.component this={HelloWorldComponent} />`.

This becomes a problem, when you build composed components that decide their layout based on logic thats too complex to express in CSS or Sveltes templating system.

For example:

- A component for tabbed navigation that automatically becomes a sidebar on desktop.

- React Router defining its entire navigation tree [1]

- Ant.Design defining the columns of the table, where you can trivially supply a short callback to render a cell [2]

[1]: https://reactrouter.com/en/main/routers/create-browser-route... [2]: https://ant.design/components/table


> maybe someone can shed some light into the downsides of Svelte

The automatic handling of dependencies and recomputing means you have less control over how updates work.

Also I think it is important to understand the mutations going on in your ui. Having them explicit like in Vue makes it easier to understand what is gonna happen by looking at the code versus the implicit way that Svelte uses.


Two-way data binding. 'Nuff said.

Instead of easy-to-trace functional logic and pure functions you have a thing more like an electrical circuit, or like a highly advanced jQuery contraption. It's fine and even great for smaller bits of interactivity. It becomes increasingly unwieldy as the scope grows.

(And for tiny bits of interactivity there is HTMX, wonderful in its own way.)


You need to learn a custom DSL for logic instead of using just Javascript. This makes it worse for TypeScript as well as other tools like for linting, etc. Personally I'll never learn another DSL after JSX.

Implicit 2 way data binding which makes logic hard to follow in larger projects, same with their reactivity model.


It really isn't that bad. You can frontload your logic outside inside the script section (or use @const's). The DSL is just a couple of {#if} and {#each}'s.

Is the 2 way data binding implicit? As I understood, you need the "bind:" keyword for 2 way data binding.


My experience:

JSX is the best templating language.

Svelte hides complexity and this can bite you in tracking down issues.

React just feels better to me for a professional project, though I like Svelte.


Just note that I last used svelte/sapper 2 years ago. I have no idea if core updates or sveltekit or (hopefully) improved typescript support changes anything.

Some types of errors cause your application to halt (which might feel like a UI freeze). There's no way to catch these errors and show an error page, show an error message, or report the error to an error tracking service like Sentry. I'm subbed to the github issue for this and never saw a fix.

Poor composability of components. Components in svelte are awesome in the general sense, they're just missing this higher-level code organization possibility that react, solidjs, and vuejs users have cozy versions of.

The docs claimed a specific function worked like nuxt's asyncData function. Whoever wrote that has no idea what nuxt's asyncData function does, because that function worked very very very differently.


Well it is very new. React has a massive inertia, so it dominates for now. I do FE work and see Svelte is gaining popularity.


2016 is not very new


Probably as much as the 3 year difference is the fact that Facebook made and evangelized React. I know I didn't hear about Svelte until 2018-2019, but I knew about React within a few days of its release.


Actually, that's very old in frontend years.


The problem is, there is no panacea in technology, only tradeoffs. Svelte does look pretty sweet though.


> Angular’s change detection is a disaster. The developer gets two suboptimal choices: (1) the slow and naive default implementation, or the complexity of managing change detection manually.

This is completely wrong. The "naive" approach is the one you should always use, with the onpush strategy reserved for breaking certain cascading situations manually. But the default approach works perfectly fine, it is performant enough, and even more when compiled for production, as it runs only once (as compared to twice in the debug mode). The author is incorrectly assuming that the change detection is slow because it is automatic. That's the entire point of the framework btw.


We've never had an issue with the Angular change detection. Large scale projects, multiple teams touching a modular front end. Its a win!


Agree. That part reeks of "that's what I've heard somewhere, never bothered to check". I work daily on angular projects of reasonable complexity and the performance of the automatic change detection is perfectly fine.


You know what I've always wanted in a front end framework, as a back end developer that sometimes is forced to work on front end tickets? I want a development build mode that generates some kind of project metadata where I can just point to something on the screen and get a report of all the interesting files in the project that are responsible for what I'm seeing:

* API calls

* Templates

* CSS

* Controllers, etc.

My usual technique of finding some text content, looking for an i18n file and working my way backwards is tedious.


Most of the major frameworks (I'm 100% sure about Vue and React) have browser extensions that give you extremely powerful dev tools. It's not _exactly_ what you're asking for, but pretty close. You can actually click on a random thing, see which component it is, what kind of state it's holding internally etc.


Apologies if you're aware of this already but every browser has a web inspector built in where you can point and click on something and at least see the HTML and CSS associated with it.


And vuejs has a browser plugin which can sort-of show you which components call which components to get there.


React and Redux both have similar addons as well, to inspect the component tree and to see the state changes over time (and even roll back; "time travel debugging" has been one of Redux's primary features since it was released).


Directly surfacing the effects involved, e.g. HTTP calls via all the event handlers through layers of nesting, would indeed be nice.


I’m not sure how other browsers’ dev tools do on this front, but at least in Chrome most requests in the Network tab will link to the file/line of code which initiated the request. From there you should be able to set breakpoints or whatever to track subsequent requests.


and you can also click on a request in the network tab and find out the line of code triggered it, including callstack

also with react dev tools you can click somewhere on the page and see the react component, it's props, etc. similar tools are available for other frameworks


Sveltekit has a thing now where when I do the standard ctrl shift c and highlight something, it’ll open the file in vscode responsible for it. It’s honestly a little bit unsettling, but I think it’ll be useful with some practice.


You may be looking for `sourcemap`s, which store a record of the transpilation that has happened and let you work backwards. Your browser's tools should fetch them by default if they're available, but depending on your frontend stack, they may not be generated by default.


That is putting the cart before the horse. Frameworks solve for a fundamental business problem well before ever approaching any technical problems. A front-end framework is an architecture in a box, a pre-designed composition. There are a couple of business reasons for that:

1) Provides a common externally defined abstraction, so that developers require less training and are more disposable.

2) Supplement skills of unskilled developers

The reason frameworks do not primarily solve for change detection, is because the browsers already provide that. Frameworks provide an additional solution riding on the browser's solution as an abstraction. Framework don't even really solve for a common set of standards either, because the browser provides that too. I really want to say that major frameworks provide for a more narrowly construed set of APIs and design approaches, but the APIs on modern frameworks are absolutely massive, so I cannot say that either.

So... frameworks are really just an abstraction to achieve a particular design approach, whether good or bad.


3) Saves you from having to rewrite dataflow and state management code with every project. (Saving time is a business factor.)


Those are trivial to solve for and then once you do solve for it its just a matter of copy/paste from project to project with about 10 minutes of rewiring. That costs dramatically less than spinning up a large framework project to project, but frameworks save on training time because most developers cannot solve for these problems on their own.


You're describing an inhouse framework


That is called Affirming the Consequent. Something like: all frameworks are composed of code so therefore all code eventually forms frameworks. It's a common form of nonsense.

https://en.wikipedia.org/wiki/Affirming_the_consequent


What is the difference between code you've written that you use from project to project and a framework?

If it is just about the knowledge then wouldn't it be valid to just gain that same knowledge about a framework?


Component isolation.

* State management is merely a sum of few parts: a state artifact (a big object), a means to update that artifact, a place where that artifact is saved, and finally a function to apply state on page reload. Its an isolated system. If anything wishes to update state it just calls a function.

* GUI: A window system (a big function and a bunch of supporting functions for events), content, and events specific to types of content.

* File system: A call to the file system library from a user interaction, a library to recursively walk the file system on the back end, a messaging payload to communicate the requested file system details, a function to populate the file system data into DOM artifacts for user interaction.

And so on.

I prefer to work without frameworks because they execute far more slowly, get in the way of solving real problems (complexity), and they slow me down in writing/testing code. Writing applications isn't about how to write code. It's about organization, flow control, and just connecting the dots between a problem and a solution. Its the difference between repeating minimum literacy and writing an original inspiring novel. Why would I think some giant abstraction would save me time or improve my code?


This website loads staggeringly fast (much faster than HN and my own static site which uses Gatsby and is hosted on Cloudfront). What’s the reason for this?


The whole HTML is likely static, and it has only one <script>, at the very end, that attaches some async update handler to the existing DOM. No wonder it renders instantly.

Modern computers are obnoxiously fast, and so are modern browsers. But modern websites overload them with a sickening amount of processing and network latencies before displaying the content.


It flashes a dark theme for a half a second before switching to light


Served from a service worker after the first load


Served via service worker, but still Cloudflare serves it faster than my very minimalistic static blog. Perhaps it uses paid Cloudflare plan?


Really impressive stuff. Seems to have slowed down slightly now that it's on the front page, but blazing fast when I first opened it.


Impressive? This is the default. You have to actively do stuff to make it slower.


You have to actively do stuff to make a house messy, but it can still be impressive when it’s clean.


I find it the opposite. You have to actively clean a house, but do nothing and it won't be clean


"Tidy" may have been a better word than clean.

The natural state of a home is tidy. It was probably empty when the current resident moved in. The natural state of a website is fast. A tiny website can be delivered in one packet.

The home only becomes messy through action. Not intention, but action. Someone has to dirty a dish for there to be dirty dishes. And it's not like you can avoid these activities, that's what a home is for.

It requires a countering action to put things back as they were. So it's action on both sides. Action to make things messy, action to keep things tidy. It's the second action we admire in the tidy home, the fast website.


Interesting. I'm seeing something slower. Pagespeed shows 2.7 seconds:

https://pagespeed.web.dev/analysis/https-themer-dev-blog-the...


Even just an eye-test shows it's much faster than that for me.

Pagespeed shows .6 for desktop, but locally I'm seeing frames loaded under 350ms. I would have guessed much faster, but I guess the devtools don't lie.


I think devtools can lie in the sense that it can be slower when devtools is open in some circumstances.


PageSpeed Insights tests on a slow internet connection and an underpowered device


52ms for me. Pretty standard for an HTML page.


I don't think I've ever seen a page load in 52ms (`load`, not `DOMContentLoaded`) with caching disabled.


QML wins with bindings and signal and slots! (In this example the the text property of the Text componenet is binded to count, so whenever count changes the text changes).

```

property int count: 0

Button { onClicked: { count--; } }

Text { text: count.toString(); }

Button { onClicked: { count--; } }

Button { onClicked: { incrementLater.start(); } }

Timer { id:incrementLater interval: 1000 onTriggered: { count++; } }

```

Better in so many ways.


Having used both QML and Svelte, I enjoy Svelte a lot more. The reactive APIs are very similar, just declare variables, mutate them, and use them. But Svelte is much nicer for many reasons:

1. It uses modern JavaScript and not some limited custom ES5-like JavaScript

2. Svelte can be used with TypeScript so your UI code can be statically typed

3. The integration with Svelte and VSCode is much better than QML and Qt Creator (the big thing is that IntelliSense is much better at analyzing JavaScript code than whatever Qt Creator uses)

4. Having an actual live reloading dev server is so much nicer than compiling and running for every change

5. Svelte has much better documentation than QML

These are just the first things that came to mind, but I could probably keep going for a while. Do you think I'm being unfair to QML? And/or have you used Svelte?


1. Yes, but I never found it limiting. If I need to write some complex logic I mostly do it on the C++ side (that's should probably be the best way anyway).

2. True.

3. Yes, Qt Creator is very lacking in that department. There are many instances where it doesn't recognize certain commands and you need to guess if you're writing correctly or not.

4. Live reloading is possible in QML. See[1] (But this should definitely be the default in Qt Creator).

5. I agree that QML documentation is lacking. That's probably my biggest pain point. Some of the examples also use deprecated/bad-practice code which is annoying (eg, using that magic `index` property rather than defining it as a required property.

I think you have fair points. I actually have a doc with similar things that QML needs to improve in. I never tried Svelte but I'm familiar with React, and I must say that Qt/QML is a breath of fresh air after using React. Developing in React always felt hacky like using duct tape and glue compared to building something properly which QML/QT feels like.

Apart from the points above, I still think QML has been great for my productivity and I'm impressed how easy it is to develop with it complex UIs easily. I've been developing a feature in my Qt C++ app in QML that turns Markdown tasks into Kanban[2]. It worked out perfectly. The tasks processing is done in C++ which sends the data to QML.

[1] https://github.com/patrickelectric/qhot#integrate-in-qtcreat...

[2] https://i.imgur.com/C1O4Nbu.gif


Thanks for the thoughtful response. It's interesting hearing from someone who actually likes Qt. I use both React and Qt/QML for work, and I have a hard time enjoying the Qt/QML work. Maybe I'm missing something or doing things wrong?

That reloading tool is cool, though its not quite as useful as modern dev servers. It seems like this simply restarts the app on every change, but modern dev servers remember your state too.

I'm curious to hear more about your React experiences. I don't love React, but I still prefer React+TypeScript over Qt+QML. With TypeScript on the strictest setting, programs are much safer than with QML or Qt. We both know QML is very unsafe. And after using TypeScript and Rust, I'm realizing C++ is a very unsafe language too. It has unexpected nulls, memory leaks, seg faults, etc. I find C++ such a rough language to use, and that could be it's own mega rant. Qt's crazy macro system makes things even worse and generates some crazy errors.

There are so many options for building great cross platform UIs now. Tauri, Flutter, React Native, etc. Those all seem to have a better dev experience than Qt/QML. If performance and safety are really important, then you can use Tauri and write your critical code in Rust. There are also UI frameworks for Rust popping up like Dioxus so you can write everything in Rust.

Your "Better Notes" app is amazing and that Kanban is looking amazing. Though, I feel like you're a great dev making great stuff albeit Qt/QML, not because of Qt/QML.


Perhaps your experience with QML would be more pleasant if you would write backend in Rust or Julia (my current choice). In Julia I find that it is particularly easy to iterate as it avoids any compilation step although that is not as smooth as using VS Code with hot reload on the side. There is also Felgo for reloading which I guess also preserves the state, but I have not tried it yet.

One quite an advantage for QML is how easy it is to align elements to get started. Personally, I find it quite frustrating to get HTML to do what I want whereas in QML having only used for two months I already see how to model `Kanban` UI with QML.


I still don't get what advantage QML has over something like Svelte. Writing the QML backend with Rust would be nicer, but why wouldn't I just write my front end using Svelte/TypeScript with something like Tauri at that point? With statically typed languages you get autocomplete, more instant feedback, more editor integration for things like renaming functions variables, and the end result won't have type errors at runtime.

Felgo is what I was hoping for, good find! That covers one pain point, but there's still many, many more. QML does have a simple and great list of components to get started with, where as HTML and CSS have accumulated many years of cruft. But, that being said, aligning elements into rows and columns is easy using CSS flexbox[1]. Flexbox has the benefit of being much more versatile than QMLs layout tools as well. I imagine using flexbox you could make the Kanban UI just as easily, and you'll probably run into less limitations.

[1] https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layou...


The flexbox does not seem to match the power of QML. For instance, I can have a two-pane view as simple as:

Text {

    anchors.top : parent.top

    anchors.bottom : parent.bottom

    anchors.left : parent.horizontalCenter

    anchors.right : parent.right

    anchors.topMargin : 10

    text : "Hello World"
}

which does not reference any dimensions of the objects. It is easy to reference an anchor of a sibling and other parent elements when needed.

Also, making new components is easy; I can inherit properties of a parent element, add my properties and put them in a file for later reuse is something I use extensively. Building each component carelessly using whatever mockery to get the look I need and then isolating it with its own namespace is relieving.


Thanks for the nice response!

> The flexbox does not seem to match the power of QML. For instance, I can have a two-pane view as simple as:

I'm a bit confused by this example. When you make the other pane wouldn't you need to define all of its anchors too? It seems like the more standard QML approach to a two-pane is using RowLayout[1].

  RowLayout {
      anchors.fill: parent
      Rectangle {
          Layout.fillWidth: true
          Text {
            text: "pane 1"
          }
      }
      Rectangle {
          Layout.fillWidth: true
          Text {
              text: "pane 2"
          }
      }
  }
I agree this is pretty nice and intuitive, but flexbox makes this problem easy too.

  <div style="
    width: 100%; 
    height: 100%; 
    display: flex;
  ">
    <p style="flex: 1">pane 1</p>
    <p style="flex: 1">pane 2</p>
  </div>
Yes inline css has problems, but I didn't want to scare you away with Tailwind[2] and I think inlined CSS makes this example clearer.

> It is easy to reference an anchor of a sibling and other parent elements when needed.

It seems like by composing layouts in QML you can avoid a lot of manual anchor setting. Though layouts do have their limits. Is that why you resort to setting anchors manually? Flexbox is much less limited and flexbox layouts compose really well.

Do you have examples of UI layouts you feel QML solves intuitively that flexbox can't solve intuitively?

> Also, making new components is easy; I can inherit properties of a parent element, add my properties and put them in a file for later reuse is something I use extensively

Svelte works the same exact way. Well, except for the part that new components do not inherit from existing components. Svelte and other modern UI frameworks prefer composition over inheritance[3]. Composition allows you to be just as expressive, but without the footguns of inheritance. If you have a specific UI problem you think inheritance solves well, I'd be happy to try and create a matching composition based solution.

> Building each component carelessly using whatever mockery to get the look I need and then isolating it with its own namespace is relieving.

Svelte uses file based components as well. Each file is its own module in TypeScript/JavaScript.

If you have rebuttals or other additional things to add I'd love to hear it!

[1] https://doc.qt.io/qt-6/qml-qtquick-layouts-rowlayout.html

[2] https://tailwindcss.com/

[3] https://en.wikipedia.org/wiki/Composition_over_inheritance


> Do you have examples of UI layouts you feel QML solves intuitively that flexbox can't solve intuitively?

I think your example on the flexbox use for two pane view illustrates that well. Although I have no experience of using it I get that the gist of it is the same of a RowLayout. It’s a good abstraction for listing many items of the same kind which need to respond dynamically with the change of window dimensions. But when I really need to place two panes 10px appart or put elements on top of it in the bottome left corner, bottom right corner and align margins of the content within the pane anchoring elements manually is so much easier. I remember when I started to learn QML my first instinct was to find where can I make a table and align elements within it where instead I just needed to use anchors.

The beauty of QML is that I can treat RowLayout and Row as elements written in QML itself. If I recall accurately it is possible to loop through child elements and assign properties to them such as anchors with respect to sibiling elements. The abstraction thus is more fundamental.

> Svelte and other modern UI frameworks prefer composition over inheritance

Inheritance is the right abstraction for the UI elements. For instance I don’t need to predict all properties in advance for my custom bottom implemntion when used in the code. Assuming that all of them are available is quite powerful and allows to experiment in place. This work exceptionally well in QML but not in OOP languages in general. Nevertheless, can you come up with `footguns of inheritance` to which QML is susceptible to?

> Svelte uses file based components as well. Each file is its own module in TypeScript/JavaScript.

This sounds interesting. I will give it a try on someday when I will need to make an UI in HTML.


> two panes 10px apart

`gap: 10px`

> put elements on top of it in the bottom left corner, bottom right corner and align margins of the content within the pane anchoring elements manually is so much easier

These instructions seem a bit more vague. If you make this visually or with QML I'm sure I can replicate it in an intuitive way with flexbox.

Flexbox has `align-items`, `justify-content`, `align-content` which covers all the needs of aligning to centers or corners. `padding` and `margin` work exactly like you'd expect. Unlike QML's layouts, you can also enable `flex-wrap` so content automatically wraps when it overflows the allocated space.

Maybe you're also wondering, how do I memorize all these flexbox properties? I don't! Chrome dev tools give you a GUI for editing a flexbox[1], isn't that really nice?

With all these tools, hopefully it becomes clear that you never need to manually define anchors. You can compose these layouts to build whatever complex layouts your heart desires. But, if you are still really attached to the anchor workflow, you can do that with CSS as well. For example, `position: absolute; bottom: 100%; right: 100%` would anchor a child element to the bottom right of its parent.

> The beauty of QML is that I can treat RowLayout and Row as elements written in QML itself.

I totally agree here. My Svelte example from above would become:

  <RowLayout>
    <p>pane 1</p>
    <p>pane 2</p>
  </RowLayout>
What if I told you it's totally trivial to make this RowLayout component yourself in Svelte? This could totally be valid Svelte code! If this sounds interesting, this example[2] is a great starting point. And it would be even more trivial to pull in a great component library like Skeleton UI[3] which does stuff like this for you.

> If I recall accurately it is possible to loop through child elements and assign properties to them such as anchors with respect to sibiling elements. The abstraction thus is more fundamental.

Hopefully this RowLayout example makes it clear that Svelte can also loop through child elements and assign properties.

> Inheritance is the right abstraction for the UI elements

How are you so sure about this? React is one of the most popular and successful tools for building UI elements, and it avoids inheritance entirely. Do you think React's approach is totally offbase? Similarly, Rust and Go do not have inheritance at all. Do you disagree with their design?

> For instance I don’t need to predict all properties in advance for my custom bottom implemention when used in the code. Assuming that all of them are available is quite powerful and allows to experiment in place.

You can expose properties with composition based elements too! You don't need inheritance to support this workflow.

> Nevertheless, can you come up with `footguns of inheritance` to which QML is susceptible to?

I think the composition over inheritance wiki page[4] explains things much more elegantly than I ever could. Though I do think I made a mistake. Does inheritance actually exists in QML land? I don't think so.

It feels like you compose things very similarly to Svelte or React. Maybe it's a just a semantics thing, but imagine HelloText.qml:

  Text {
    text: "hello world!"
  }
Does HelloText inherit from from "Text"? I'd say no. HelloText simply instantiates Text and sets its' text property to "hello world". Though I imagine you would say HelloText inherits from Text and overrides the text property?

Compare this to the Qt Widget world where the difference between inheritance and composition are much more clear. Sometimes a Qt Widget doesn't expose the properties or functionality you want. The best scenario is when Qt Widgets do expose the properties you need. Then you can instantiate them and change those properties. Notice that even Qt Widgets sometimes expose properties without inheritance! When defining bigger fancier widgets, why require inheritance? Break them into smaller parts and expose useful properties. Then you and other programmers can compose custom behavior without going through the sludge of inheriting and overriding!

Perhaps we already agree on that part and it was just a semantics issue about whether QML is actually using composition or inheritance?

> This sounds interesting. I will give it a try on someday when I will need to make an UI in HTML.

Yay, I'm interested to hear what you think!

[1] https://developer.chrome.com/docs/devtools/css/flexbox/

[2] https://svelte.dev/examples/slots

[3] https://www.skeleton.dev/

[4] https://en.wikipedia.org/wiki/Composition_over_inheritance


> Maybe you're also wondering, how do I memorize all these flexbox properties? I don't! Chrome dev tools give you a GUI for editing a flexbox[1], isn't that really nice?

This is quite nice indeed. I didn’t know that Chrome dev tools can be used that way.

> Does HelloText inherit from from "Text"? I'd say no. HelloText simply instantiates Text and sets its' text property to "hello world". Though I imagine you would say HelloText inherits from Text and overrides the text property?

Note that is also possible to create a new properties and signals for the Text which can be referenced by the children. Also it is possible to override the default method on how to treat it’s children for instance if you want to implement a CustomRowLayout. The HelloText thus has properties of the Text and the newly defined ones and it is possible to instantiate it at multiple places with different parameters and children. I don’t see why this is not inheritance…

> Perhaps we already agree on that part and it was just a semantics issue about whether QML is actually using composition or inheritance?

In QML composition is when one places children elements within a component and expose relevant properties and signals to the parent. So I disagree on this part. QML in my opinion is OOP done right.


> Note that is also possible to create a new properties and signals for the Text which can be referenced by the children

This behavior exists without inheritance as well! Let's imagine we want to use our existing `HelloText` component. But like you suggested, let's subscribe to the `onclick` signal and add a `bolded` property. When the user clicks the text, it will toggle the bolded property. We will use React because it's famous for not using inheritance.

```jsx function HelloText(props) { return <Text {...props}>Hello World!</Text>; }

export default function App() { const [bolded, setBolded] = React.useState(false); return <HelloText onClick={() => setBolded(bold => !bold) bold={bolded}} /> } ```

And just like you wanted, even children of `<Text>` could reference `bold` and `onClick`. I'll assume you know what that would look like. If not, let me know and I'm happy to make examples for that too. You can provide default prop values that can be overridden. You can accept functions. You can expose signals.

If you are being attentive, you might have noticed the code that enables this behavior: `{...props}` is the explicit way to pass all the props from the parent `HelloText` to its child `Text`. Unlike React, exposing props is implicit and automatic in QML. Though its trivial to make every component expose all their props to their children, the docs argue that you should avoid this. They say that if you find yourself doing this a lot, you are probably designing things poorly [1].

Here's how I understand this: If you are going back and wishing you exposed more properties or methods all the time, then you are probably developing things top down instead of bottom up. When building UI, it's easier and better if you start by building the smallest possible pieces of your UI. Then you can compose them into bigger, more complex pieces. The other way around is dangerous because you would be designing inherently inflexible abstractions. Even the most elaborate and beautiful castle made of yarn will never be as flexible as a castle made of legos.

For example, when and why would you want this `CustomRowLayout` component? Instead, simply start with the smallest easiest parts of your app. Build the buttons inside that row. Maybe the row has a dropdown item, so you can build that. Compose those parts using `RowLayout`, `ColumnLayout`, `GridLayout`, and `Flow`. Maybe make yourself yourself a `Spacer`. While you build your apps' custom rows in this pattern, you will 1. naturally develop a great toolbox of composable components that are flexible and useful and 2. have a much easier time than reimplementing some method on a `CustonRowLayout` component! Previously you asked me for an example where inheritance is a footgun in UI development. A `CustomRowLayout` is an example as building new row layouts bottom up is clearly superior.

But also, React and other modern UI frameworks won't stop you from building `CustomRowLayout` if that's what you really want. Hopefully now it's clear that you can build something like this using React, Svelte, or any other modern UI framework without inheritance. And also, if you find yourself doing this often, maybe that's a sign you are approaching problems from the wrong direction.

> In QML composition is when one places children elements within a component and expose relevant properties and signals to the parent. So I disagree on this part

Honestly, I think this is a fair distinction and definition. Though, then things get a bit hairy on the other side. Clearly React and Svelte support the alternative behavior that you call inheritance in QML. What do you call that now? Did React and Svelte suddenly become OOP frameworks too?

I think the important conclusion is that building complex UI using composition in a bottom up fashion is 1. possible and 2. optimal. QML, Svelte, and React all allow developing top down and bottom up, which is great. Svelte and React simply make top down development explicit.

I will leave things with a message from React's composition vs inheritance page: "At Facebook, we use React in thousands of components, and we haven’t found any use cases where we would recommend creating component inheritance hierarchies.

Props and composition give you all the flexibility you need to customize a component’s look and behavior in an explicit and safe way. Remember that components may accept arbitrary props, including primitive values, React elements, or functions." [2]

[1] https://react.dev/learn/passing-props-to-a-component#forward... [2] https://legacy.reactjs.org/docs/composition-vs-inheritance.h...


BTW, someone created Flexbox for QML: https://github.com/tripolskypetr/qml-flexbox


It's really awesome someone did this. If you just want the expressiveness of flexbox and don't care about performance, this is perfect!

However, wouldn't the performance for this be very optimal? Check out the `Flex.qml` code[1]. It's all very unoptimized and dynamic QMLScript. None of the function arguments are typed. There is no way the QML compiler is turning any of this into efficient, statically typed C++ code. The beauty of using flexbox in the browser is that all the layout calculations are done natively in the browser without ever touching JavaScript.

[1] https://github.com/tripolskypetr/qml-flexbox/blob/master/qml...


Nope, from what I can tell the entire backend is a Qt C++ binding[1] for the C++ Yoga library[2][3]. You can see the Qt C++ backend is exposed to QML here[4]. So it's C++ all the way down just in a comfortable setting.

[1] https://github.com/tripolskypetr/qml-flexbox/blob/master/obj...

[2] https://github.com/tripolskypetr/qml-flexbox/tree/master/thi...

[3] https://github.com/facebook/yoga

[4] https://github.com/tripolskypetr/qml-flexbox/blob/677a1287df...

P.S. I'll answer your other reply very soon. I'm releasing a new version of my app so it takes its toll on me.


Whoops, I should have been more clear. When I said "It's all very unoptimized and dynamic QMLScript" I meant just the file `Flex.qml`. I'm sure the Qt C++ bindings are fast and great, but check out the `updatePositions` function in `Flex.qml`[1]. This unoptimized and dynamic QMLScript gets ran every time a flex item's width, height, or children change. I imagine if that QMLScript was given types then at least that QMLScript compiler could generate some C++ for it, but right now there are no types so I doubt there is any efficient C++ generated for this script.

On the web it's common to animate the size of flexbox items with CSS animations. These animations are entirely implemented by the browser, and I imagine much of it is even GPU accelerated. No JavaScript is executed for this on the browser. But with this library, you would be running the `Flex.qml`'s `updatePositions` function every frame of the animation. Isn't that wasteful when compared to how flexbox works in the browser? It seems like a browser would be much faster at computing layout for flexbox elements than this, especially when that flexbox's size is being animated with CSS animations.

[1] https://github.com/tripolskypetr/qml-flexbox/blob/master/qml...


Hmm I get your point. I wonder if the the QML type compiler (qmltc) could still compile to C++. It would seem like dark magic if it did tho, because as you said it hasn't given the types. I wonder if it's possible to put this code in the backend in C++, what do you think?


> I wonder if it's possible to put this code in the backend in C++, what do you think?

Yeahh, looks like you'd have to connect to all these signals and be able to access sizing/position from C++. Not sure how possible this is, you probably know better than me.


Thanks for the kind words!

> Maybe I'm missing something or doing things wrong?

To be fair, I have many annoyances with Qt Quick. I think many of the components don't look and work native out of the box, requiring significant customization that is time-consuming compared to what you get out of the box by using many libraries alongside React. And there are these other annoyances that we already talked about. But in my view, the QML paradigm is extremely consistent, even migrating from Qt5 to Qt6 was quite straightforward. I just updated my website to a new NextJS version and so many things broke in React there were serious paradigm changes if I remember correctly. I also enjoy working with Qt's Model/View, I appreciate the native performance I get from compiled C++ code (Most QML code is compiled to C++)[1], the community is very helpful, etc.

> I'm realizing C++ is a very unsafe language too.

Yes, and I think programming languages like Rust are very cool. I know there are Qt bindings for Rust. But I'm not sure how QML is unsafe. Probably because it compiles to C++? Converting Qt Core itself to Rust is quite an initiative that I'm not sure is necessary. But what is cool is that you can write your UI in QML (compiled to C++ somewhat safely if you trust Qt) and write your app logic in Rust[2]. I hope to see more things like that.

Regarding my React experience, I developed a fully working online marketplace in React + NextJS (but discarded the idea), a React Native app that finds rhymes for words (but didn't publish because chatGPT does it better in a way), and my personal website[3] is in React + NextJS - I love this combo for developing websites, I find it so easy and the integration with Vercel is amazing.

I like React for small projects but as it gets bigger I find it quite cumbersome. Though if we're talking about websites I guess it's one of the best approaches (although Svelte looks good but I never tried it).

React Native is so good because they cracked it with components behaving and looking like native components (well, they are the native components I guess). But I prefer to work with Qt/QML. I thought it would take me a long time to learn it but I bought a good online course from Udemy and in a day I knew all the basics, the next day I had a working prototype for my Kanban.

I tried Tauri as well, it sounds good on paper but when I tried it I didn't really like it. I don't remember why but I remember wrestling too much with things not related to actually writing code.

I keep an eye on Rust GUI frameworks but haven't seen something promising yet. I never heard of Dioxus before so I'll check it out, thanks!

[1] https://www.qt.io/blog/the-new-qtquick-compiler-technology

[2] https://www.youtube.com/watch?v=0HEJFYSxbB8

[3] https://www.rubymamistvalove.com


I'm the author of rust binding for QML (the qmetaobject crate). The idea is that this is "safe" because your Rust code is safe, and QML is also supposedly safe. The implementation of QtCore/QtQuick is not your code to maintain. So hope that other people made it safe. See it as an abstraction is just like the Rust standard library that uses lots of unsafe.

But anyway, I'm also making Slint [0], a toolkit developed in Rust, inspired from QML. So that even the implementation of the toolkit is safer.

[0] https://slint.rs


> is not your code to maintain

Exactly, that's what I meant, if I trust QtCore/QtQuick, writing the UI QML and logic in Rust is a pretty good idea. Although I must say that the latest Qt versions Qt 6.5.0-6.5.1 came with two serious bugs for me causing me to revert to Qt 6.4.3. So even that comes with a grain of salt.

I have been keeping my eye on Slint! I must admit, the use cases shown on your website don't look attractive. First, the apps don't seem useful (some random analytic panel, printer demo, and widget gallery) and they don't look good. If I may suggest, put there a simple TODO app that everyone is been taguht these day how to develop one in all the new frameworks. Maybe another app similar to that. It's going to be much more practical and people can have a sense of what is possible to do in Slint. In any case, I have been coming to check Slint every few weeks to check the progress so it's very cool to speak with the author. I wish you the best with it! If you need more feedback on the website's UX or anything don't hestiate to reach out: ruby . mamistvalove at gmail . com


> But in my view, the QML paradigm is extremely consistent, even migrating from Qt5 to Qt6 was quite straightforward. I just updated my website to a new NextJS version and so many things broke in React there were serious paradigm changes if I remember correctly.

Totally fair points here. Though slight nitpicks, these breaking changes were probably entirely NextJS changes and not React changes. NextJS solves a much more complicated problem, full stack web apps. I think it's more fair to compare QML with React as they both focus on simply describing UI.

> I appreciate the native performance I get from compiled C++ code (Most QML code is compiled to C++)[1]

I read the article and I'm skeptical that most QML code is compiled to C++. My understanding from the article is that they are able to compile VERY basic QML code that you manually specify all the types for. The problem still remains that QMLScript is a very dynamic language. QML doesn't even allow you to type your list or object properties! How much of your QML code doesn't touch lists or objects?

> But I'm not sure how QML is unsafe.

QML is unsafe because it's a dynamically typed langauges. Many errors will not be discovered until runtime! I brought your example over to qmlonline[1] and modified it.

  Rectangle {
      property int count: 0
      
      Button { onClicked: { count += "1"; } }
      
      Text { text: count.toString(); }
  }
This "compiles" fine, runs fine. In fact, it perceives this as fine behavior and continues on without even giving a runtime error when I click the button??

Let's try something even more clearly bad. Let's do `count = "test"`. This "compiles" and starts just fine too?? Finally you get a runtime error when you actually press the button.

I'm really skeptical about how or what C++ this all compiles too, because this is all very much dynamic behavior, and it's not giving us any useful feedback at compile time. These are really simple errors that this "QML compiler" should be catching and presenting to the developer.

These kind of errors are impossible with TypeScript. TypeScript will tell you immediately in your editor when you type this mistake, so you will never get runtime type errors. Going from TypeScript to QMLScript or regular JavaScript is probably the biggest downgrade of all to me. C++ is statically typed thankfully, but what good is having only half of your code statically typed?

> I like React for small projects but as it gets bigger I find it quite cumbersome.

Reading this and "developing in React always felt hacky like using duct tape and glue", I wonder how much of this is actually React and how much is using dynamic JavaScript. If your whole app was dynamic QML code, you'd probably feel like that was hacky too, right? If you have specific examples of things that felt hacky and hard in bigger projects, I'd love to hear that. My hypothesis is that you'd feel like your React projects were much more "solid" and "proper" if you used TypeScript. The facts are that your users would run into much less runtime errors with TypeScript code than C++/QMLScript code. Also, TypeScript provides much more immediate feedback while developing.

I'm curious about what you think QML really excels at. It sounds like it was perfect for quickly making a Kanban prototype? What all did the prototype include? Maybe I should try recreating some portion of it with Svelte. I think it could be interesting to have a direct comparison. If QML has useful tools for building UI that Svelte doesn't, I'd find that really interesting. I'm constantly looking for the best way to build UIs, and right now Svelte seems like the best.

If you remember the specific problem with Tauri, I'd be interested in hearing that. I'm curious about the performance differences between Tauri and Qt apps. There are probably some pros and cons with Tauri's web view approach. I imagine OS native web views might be better at rendering certain UIs than Qt's rendering.

It's cool that you're looking into Rust things too. Rust gives even better compile time feedback than TypeScript which excites me. It's super interesting hearing all your thoughts, and would love to hear more.

[1] https://qmlonline.kde.org/


> Though slight nitpicks, these breaking changes were probably entirely NextJS changes and not React changes.

Yes, you're right that's probably the case.

> I read the article and I'm skeptical that most QML code is compiled to C++.

Well, all the core Qt Quick components are written in C++[1]. Other components either reuses them in C++ or QML. So they're mostly C++ underneath.

> QML doesn't even allow you to type your list or object properties! How much of your QML code doesn't touch lists or objects?

What do you mean? JS object (like JSON?)? I'm quite sure QML supports object property. And JS list? There's this[2].

> QML is unsafe because it's a dynamically typed languages.

I get your point. Although there is qmllint that warns you of many such things before compilation even[3]. It's great. It's a shame they don't easily integrate it into Qt Creator by default. It saved me many errors. In your first example, I wonder why it does its behavior where it concatenates an int like a string. That's weird.

> The facts are that your users would run into much less runtime errors with TypeScript code than C++/QMLScript code. Also, TypeScript provides much more immediate feedback while developing.

I think that's a good take. And maybe something Qt should consider adding on top of QML.

> wonder how much of this is actually React and how much is using dynamic JavaScript.

I just remember handling state changes wasn't that natural to me (maybe it's the use of Hooks?). TypeScript does sound interesting, but I'm a QML convert for now.

> I'm curious about what you think QML really excels at

First - using Qt Quick - native performance. But talking about the language itself - I can't get over how amazing property bindings and signal and slots are for state changes. Just these alone would make me quit any web framework. They are so simple yet incredibly versatile and powerful. It just makes sense to work with them than any Hooks or whatever in React. Then the Model/View paradigm of Qt fits so well into QML is just awesome.

> What all did the prototype include?

Well, you can easily grab a binary from our GitHub Actions and try it out[4] or compile it from source. The basics for the Kanban were to 1. Process the markdown tasks in TextEdit 2. Beautiful interface 3. Beautiful drag and drop like `react-beautiful-dnd` 4. Markdown inside of tasks. 5. Rendering fast with even thousands of tasks.

> I'm constantly looking for the best way to build UIs, and right now Svelte seems like the best.

Me too. But I'm reluctant to non-native performance anymore. I had enough of Electron apps. That's why I was trying React Native (I had a good experience with it but I still prefer QML) and Tauri. I really can't remember why I disliked tauri (maybe i tried to create a custom window decoration but couldn't?). If you ever give it a shot let me know what you think about it. But then again, I'm sold on the flexibility of Qt C++ together with QML.

> I imagine OS native web views might be better at rendering certain UIs than Qt's rendering.

Why is that? Qt is using the underline accelerated graphics APIs to render its content[5]. Even if a component isn't native we say it's native-like because it still uses the native graphic API just not the native component.

> It's super interesting hearing all your thoughts, and would love to hear more.

Thanks! I learned a lot from what you're saying. I have plans to create an ecosystem of open-source cross-platform apps, so I've been deliberating for the longest time on which framework to choose. For now, I've settled on Qt/QML. It has its cons. But that's why I like talking about it with other people so I can document these, file the right reports and maybe improve it as time goes by. After releasing the next version of my note-taking app I plan to migrate the UI completely to QML and create an adaptable/responsive app that will share the same source for desktop and mobile, taking inspiration from MAUI apps[6] that our based on MAUIKit[7].

[1]https://code.qt.io/cgit/qt/qtdeclarative.git/tree/src/quick/...

[2] https://doc.qt.io/qt-6/qml-list.html

[3] https://doc.qt.io/qt-6/qtquick-tool-qmllint.html

[4]https://github.com/nuttyartist/notes/actions/runs/5565887776

[5] https://doc.qt.io/qt-6/topics-graphics.html

[6] https://mauikit.org/apps/

[7] https://mauikit.org


> Well, all the core Qt Quick components are written in C++[1]. Other components either reuses them in C++ or QML. So they're mostly C++ underneath.

Gotcha, this makes sense. To be fair, browsers write HTML elements like <input> and <textarea> using C++ too. Though I admit the core Qt Quick components cover more functionality use cases than HTML elements.

> And JS list? There's this[2].

Ooh whoops, good catch on the typed lists. I seen now you can do stuff like `property list<int> intList: [1, 2, 3, 4]`. Looks like I missed this because it was added sometime inbetween 6.2 and 6.4?

Still I'm a bit skeptical, what about lists of lists like `list<list<int>>`? And what about lists of objects? I wish I could quickly test Qt 6.5 myself without setting up the whole dev environment, but qmlonline only supports 5.15.

> JS object (like JSON?)? I'm quite sure QML supports object property.

Whoops, objects are a tricky overloaded word. But yeah, something like a struct in C. For example, TypeScript ensures this code will be totally typesafe at runtime:

  type Person = {
    age: number,
    name: string
  }

  let people: Person[] = []
  people.push({
    age: 24,
    name: "jeff"
  })
In QML it seems you need to go to C++ to define a type like `Person` and expose that to QML?

> Although there is qmllint that warns you of many such things before compilation even[3]. It's great.

Ohh, that would definitely be an improvement. It doesn't look like it does any type checking, but atleast checking for syntax and anti-patterns is something. If Qt gave QML good static typechecking like TypeScript, it would make my QML experience sooo much better. Then the big remaining problem is how unsafe C++ is lol.

> First - using Qt Quick - native performance.

I'm still a bit skeptical about all that QMLScript getting compiled to native code, and it's not like there's zero overhead in Qt's OOP code. It feels like native is a spectrum where C or Rust would be more native, right? If you use native components with C, that should be considered "true" native performance, right? I'm sure Qt/QML is more performant than something like TypeScript with Svelte. But I'm really curious to see by how much.

> But talking about the language itself - I can't get over how amazing property bindings and signal and slots are for state changes. Just these alone would make me quit any web framework.

I totally agree property bindings are great. In the same way properties are reactive by default in QML, any variable is reactive in Svelte. Signals and slots are interesting. I always thought its better to avoid them and find a better way to structure things. Signals and slots are component events[3] in Svelte, but people seem to rarely use them. I'm curious to see where you use signals and slots in QML. Maybe I'm missing out here.

Properties, signals, and slots exist in the C++ side too and I have some nightmares about these. The fact that this is all implemented using the Q_OBJECT macro means that when you make any mistakes you get some really gnarly errors. It feels like humans were never meant to see these errors. Maybe I'm just too spoiled by Rust and TypeScript error messages.

Callbacks are really useful in UI programming. They make it possible to do event driven programming. But it feels so rough using callbacks in C++ because functions aren't first class citiziens. In Rust or TypeScript callbacks feel much more sane to use. And when you make inevitably make mistakes, you get much more sane errors.

> They are so simple yet incredibly versatile and powerful. It just makes sense to work with them than any Hooks or whatever in React

I definitely agree that QML bindings are much nicer to work with than React hooks lol.

> Then the Model/View paradigm of Qt fits so well into QML is just awesome.

Ooh interesting. It seems older frameworks were more model/view oriented, but now new frameworks interconnect these more. It's interesting you enjoy this. I find the model and view rarely get changed seperately. New features tend to require changes to both the model and view, so it seems weird to seperate them.

> Well, you can easily grab a binary from our GitHub Actions and try it out[4] or compile it from source

Awesome thanks! I'm looking forward to trying this out and checking out the code. It will be interesting to see if Svelte can compete on any of these things.

> Why is that? Qt is using the underlying accelerated graphics APIs to render its content[5]. Even if a component isn't native we say it's native-like because it still uses the native graphic API just not the native component.

If we call QML/Qt components native-like, then we should also call HTML and CSS native-like. Browsers are built with C++ and render HTML+CSS with accelerated graphics APIs too. There are tons of powerful, interactive HTML elements[1] and when combined with CSS animations you can achieve a lot without ever touching JS.

Browsers just keep getting better. For example, the new View Transitions API[2] means that the browser is exposing hardware accelerated page change animations. Even with current CSS animations, there are lots of GPU accelerated animations that are either 1. not possible with Qt/QML or 2. less performant in Qt/QML.

Maybe you find some built in GPU accelerated feature QML has that browsers do not expose. No big deal, the web has a WebGL API, and the even more powerful WebGPU API is rolling out! We can write our own hardware acceleration using TypeScript!

However, I admit reality is messier than this. With QML its tempting to quickly hack together functionality with QMLScript that should have probably been done through different means. On the web it's the same, but TypeScript seems like a much more sane and safe language than QMLScript.

> I have plans to create an ecosystem of open-source cross-platform apps, so I've been deliberating for the longest time on which framework to choose.

This is so awesome, and I'm excited to see how your journey continues.

I'll definitely stay in touch and let you know how my experiments with Svelte, Tauri, and Rust go.

[1] https://developer.mozilla.org/en-US/docs/Web/HTML/Element

[2] https://developer.mozilla.org/en-US/docs/Web/API/View_Transi...

[3] https://svelte.dev/examples/component-events


> TypeScript ensures this code will be totally typesafe at runtime:

That's cool. I think QML is going in that direction.

"In addition, Qt 5.14 introduced the possibility to provide type information in function signatures, similar to how it is done in TypeScript:"[1] `function add(a: int, b: int) : int { return a + b }`

> I'm still a bit skeptical about all that QMLScript getting compiled to native code

"Additionally, in Qt 6.3, there will be another compiler: The QML type compiler, or qmltc. With the QML type compiler, you will be able to compile your object structure to C++. Each QML component becomes a C++ class this way. Instantiating those classes from C++ will be much faster than the usual detour through QQmlComponent. qmltc, like qmlsc, is built on the availability of complete type information at compile time. In contrast to qmlsc, though, qmltc has no graceful fallback in case the type information is lacking. In order to use qmltc, you will have to provide all the information it requires. qmltc will be available with all versions of Qt." [1]

I read your entire comment. It takes a lot of effort to respond here. You make some interesting points. I do think each should find the right tool for his job. I found QML very appealing. And I think it keeps improving and I hope they integrate some of the cool security features of typed languages etc. I might not be the best person to respond technically to your arguments, tho. In any case, I'd love to keep in touch. I saw you on Twitter so that's cool. I'm also (more available) on Discord. My username is "rubymamis" so feel free to send a DM (:

[1] https://www.qt.io/blog/the-new-qtquick-compiler-technology


how would the above QML example be implemented in svelte?


This QML example is implementing the example from the article, and the article already implements this example in Svelte. However, the Svelte implementation becomes even more concise when you inline the functions:

  <script>
   let count = 0
  </script>
  
  <button on:click={()=>count--}>decrement</button>
  <span>{count}</span>
  <button on:click={()=>count++}>increment</button>
  <button on:click={()=>setTimeout(()=>count++, 1000)}>
   increment later
  </button>
Play with this in the Svelte REPL here: https://svelte.dev/repl/97d42ad98e4b4a929d3c5a3de880e6fc?ver...

Doing this, I also realized rubymamis cheated a good bit on their QML version. It's missing the text for the buttons. It's not wrapped in `ApplicationWindow { }`. And there's a bug with their implementation: If you clicked their "increment later" button many times quickly then it would keep resetting the timer and only increment once.


Haha I was typing fast just to show the QML syntax. I don't really think good code is about lower amount of lines, but rather more about "making sense". And that's what I tried to show.

Regarding the timer, so in your Svelte code a new timer is being created each time? I wonder how to implement the efficiently in QML. I don't think it's as straightforward.

EDIT: Probably the best way is to expose QTimer::singleShot (C++) in QML.


Fair gotcha, yeah honestly I find QML to be super intuitive in the same way Svelte is super intuitive. It feels like two framework authors in very different worlds came to a similar conclusion.

> Regarding the timer, so in your Svelte code a new timer is being created each time?

`setTimeout`[1] isn't even a Svelte thing, it's a JavaScript API that QML would have access to if it was real JavaScript ;).

[1] https://developer.mozilla.org/en-US/docs/Web/API/setTimeout


I understand, but it's not quite necessary if Qt could add some better functionality exposing C++ code in QML. Luckily, someone created a cool library that allows you to create singleShot timer in QML as follows:[1] `lqtUtils.singleShot(5000, () => console.log("Hello!"))`

[1] https://stackoverflow.com/a/75288105/5865379

I also used his library to share ENUMs between QWidgets (C++) and QML.


you can do the same in aurelia:

counter.html:

    <template>
      <div>${count}</div>
      <input type="text" value.bind="count" />
      <button click.delegate="inc()">add one</button>
      <button click.delegate="counter--">subtract one</button>
    </template>
counter.js:

    export class Counter {
        constructor() {
            this.counter = 0
        }

        inc() {
            this.counter++
        }
    }


VanJS[1] seems to have a somewhat different approach.

The entire framework is surprisingly simple and easy to understand. In benchmarks I've seen it perform well, even better than SolidJS.

1. https://vanjs.org/


Offtopic:

The cursive italics are apparently a feature of the Victor Mono [1] font used for the full page. While it'd be amusing in Tumblr context (where cursive is used for hyperbolic emphasis), I can't fathom why one would consider it in a code context, but to each their own...

You can change it (at least on Safari) by going into developer tools, clicking any node, and removing "Victor Mono" from --font-family

[1] https://rubjo.github.io/victor-mono/


One of the first things I do on a new browser install is to disable web fonts. I've never seen a site where they improve things, often they flicker in after page load, which is jarring.


Victor is also an extremely narrow font. The author of the font motivates it as means to squeeze more code per line. Not only it would make longer lines more tolerable, but also for short lines it reduces the eye movement needed to scan the line.

However, I personally find the narrowness more tiring for the eyes to the point that it is even more distracting than the cursive.


Yeah, I would prefer my custom font with comic italics.


This also threw me off


firefox reader mode; works on mobile too


Unpopular opinion, but I just simply don't care about performance benchmarks between frameworks at all. If thats something you're actually faced with, you should be writing your own perf specific code. The only thing I care about is support. Can I hire people that know this? Is it easy to onboard? Is there a ton of really solid open source plugins and packages compatible with it? Is there an LTS version? These are the only things that really matter day to day.


There's another, often ignored differential factor for frameworks and that is ergonomics. All of the frameworks here require specific domain knowledge to pick up and understand. That knowledge typically involves confusing concepts (Svelte and Vue aren't too bad here) that waste a ton of developer time just to wrap their head around [1]. When I look at Angular, it feels like one of those contortionist boxes at a magic show.

This is why I ended up building Joystick [2]. The amount of time I was wasting trying to reason through how a framework wanted me to do something vs. actually building what I wanted was driving me nuts (not to mention, having to watch people I was mentoring who had less experience struggle even more).

[1] Meaning, beyond a trivial interface, any sort of complex UI is often (not always) burdened by the framework's lack of clear ergonomics. There's a lot of having to learn "why it did that" in the way of getting stuff done.

[2] https://github.com/cheatcode/joystick


Svelte isn't a strict win-win. It requires a compiler, and forks of JavaScript and HTML.

And it isn't actually very different from React or Vue anyway, and in fact quite a bit more limited than Vue (not necessarily bad).

Saying:

  “I’ll figure it out for you at compile time.” —Svelte
is just a non-technical, basically non-meaningful statement.

You need to know _what_ it figures out exactly. What state and what state changes are observable? These things matter more than whether it's done at compile time or runtime.

And I think the author is ascribing far more power "compile time" than what's actually happening. Svelte is not doing some kind of sound data flow analysis on your JS code to figure out what could change and rewriting your code to be observable (something not really possible anyway). It's using specific syntax conventions to make some variables into shallowly observable properties. And it adds "store" concept, which is more-or-less Signals as popularized in Solid, now Preact, and soon Lit and Angular.


> Svelte isn't a strict win-win. It requires a compiler, and forks of JavaScript and HTML.

These points weren't touched upon by the author. He compared the frameworks from one perspective and one perspective only.


There are 10 types of front-end frameworks in this world; those that are htmx, and those that aren't.


But nobody seems to use htmx. There's a lot of momentum and eagerness in the frontend world to go full blown SPA because well.. that's the thing that is done.


From June since no date on it

Shared by the dev https://news.ycombinator.com/item?id=36403909


Technical blog posts without a date should be illegal


But how will I drive traffic to my evergreen course funnel with publication dates all over my articles?

The FA actually doesn't do this, but a lot of things (including those that do very well on HN) do for exactly this reason.


Using the current date for the article /s

There must be a browser plugin that offers the date a website was first indexed.


I seem to recall a (non-technical) blog getting called out for basically generating a random date within the past month or so for a lot of its content, but that might just be a false memory.


That’s a brilliant idea, if someone else hasn’t already implemented it


> There are tons of blog posts on the internet about how frameworks differ and which one to pick for your next web project.

You can also just not pick one.

MDN is my anti-framework.


MDN has a great series of guides on front end frameworks :) https://developer.mozilla.org/en-US/docs/Learn/Tools_and_tes...


> Instead, we’ll go directly to the crux of the main problem front-end frameworks set out to solve: change detection, meaning detecting changes to application state so that the UI can be updated accordingly. Change detection is the fundamental feature of front-end frameworks, and the framework authors’ solution to this one problem determines everything else about it: developer experience, user experience, API surface area, community satisfaction and involvement, etc., etc.

I disagree. Sure that's at the core of how a framework is written, but all the developers really care about are the interfaces. The user doesn't care at all unless it doesn't work or is annoying to use.

If your interfaces are dictated by the way the event loop is written, there's something wrong that needs to be decoupled. Events should be generic and interfaces should be arbitrary to what developers want.


Since it's at the core of how a framework is written, it affects what the developers must handle and therefore the interfaces that are possible. If you know how a framework handles change detection, you already know something about the interface it must expose.


Why can't we just use Model-View-Controller for change-detection?

Views subscribe with the model to get notifications about changes to different "aspects" of the model. When they get a change-notification they update themselves by asking the model for its latest data for a given aspect.


Literally just Angular if you use RxJS, which the author evidently thinks is too hard to understand and so dismisses it.


This is precisely the approach I started on taking to rewrite my oss frontend stuff: https://github.com/mickael-kerjean/filestash/blob/master/pub...


Sounds simple, right? It fails horribly, because you lose control of how data flows through your application. One view triggers an update to a model, which will immediately trigger other views to render regardless of where they exist in the component hierarchy or whether there are more changes to make in tandem. This can cascade to other updates, and suddenly you're in a weird intermediate state caused by a race condition between two separate views that's incredibly difficult to debug.

The reason why solutions like React are so popular is because it radically simplifies state management: data flows in from the top of the component hierarchy down. When a component makes changes to state, those changes are queued up for the next consistent render loop, which ensures that nothing renders in an intermediate state. An entire class of bugs, gone.


> One view triggers an update to a model,

I think it should be only the Controller which changes the Model. That can cause views which are dependent on the "aspect" of the model that was changed to update themselves.

But a view updating itself should NOT cause the model to change and thus not cause a cascade of other views causing other views to change.


That's still a problem, because a view can trigger multiple controller updates (perhaps across controllers!), which need to be batched so that the application data remains in a consistent state.


I don't think that's the case in proper MVC design. Perhaps in some web version of it. In proper MVC AFAIK view never triggers any changes to anything, it just displays things and updates only itself, not others.


How can you build anything interactive if your views never trigger updates to state? Here's a simple example: I have a reusable filter component. It lets you enter text and choose an option. Sure, it can manage that state internally, but at some point it needs to communicate out the chosen option to its parent component, which needs to update its own state to reflect the filter value and handle the filtering. In MVC, you need the controller to maintain state, and the views need to be able to hook into it.


In MVC it is the Controller which handles all the events that happen upon the view. The view-"object" itself only does displaying, including updates to itself.

So a change in how the view looks does not trigger anything, it is user-actions caught by the Controller which do. These are the basic principles of MVC.

I don't know if you are using React or what, where the terms like View and Controller might have a different meaning, than in classic MVC.

My question more is about does React et al. use the principles of MVC and if not why not, and if yes why is a basic MVC framework not all we need to do change-handling.


> it is user-actions caught by the Controller which do

OK, and how does this work? The view is rendering the filter box, allowing the user to select an option. How does the Controller know what option was selected, if the view is not registering a click handler and communicating this up?

What ends up happening is that as your application grows, so does the number of controllers, models, and views. A user action in one view can cascade to many different controller updates, which can overlap and conflict in difficult-to-debug ways, and render inconsistent data states. Flux architecture solves this, which is roughly what React is based on.


rxjs (see M. Kerjean's sibling comment for an example) or mobx/mobx-state-tree can be used to implement something like that, and I've quite enjoyed both react+mobx and lit+mobx as a result.

(googling 'thing mobx' where 'thing' is your preferred rendering system has results in quite a lot of cases)


It is Ember.js, beautifully solving this problem since 2012.


Just leaving this here (typed without having to look it up) for anyone else who's ever maintained large Angular app which got to a certain size and complexity before anyone _really_ understood what they were doing:

ExpressionChangedAfterItHasBeenCheckedError


Interesting to compare these with the results on the js-framework-benchmark page [1] (with the usual caveats about benchmarking). Without actually running the numbers, qualitatively it looks like there are 3 performance tiers: VanillaJS (fastest); Vue, Svelte and Elm (often close to vanilla); and Angular and React (sometimes comparable, sometimes much slower).

[1] https://krausest.github.io/js-framework-benchmark/current.ht...


Yep. The author is making a big leap by falling into the "Angular slow" meme. In my experience it isn't, and it provides ease of use thanks to its bi-directional binding, as oposed to React's unidirectional binding. It is not the champion of speed though, might be even the slower out of the box, but still fast enough.


The op forgot to tell you that if you use React, you have to add a slew of 3rd party libraries to make it work and no one tests every combo. This is called “unopinionated”.

Angular is “opinionated” in that you don’t need to add anything. It definitely has a higher learning curve, but from an enterprise perspective, it’s a safer tool.

And if you have so many things on a web page that performance is a problem, then I’d suggest you need a better user experience. You should never have a design that overwhelms the framework.


It is something that distinguishes them, certainly. As someone who switched from using Vue to React at my day job, dev tooling & editor support have been the things that stand out the most to me. React works well with Vite, which has helped make it easier to use, and I've been migrating projects away from Webpack + Jest to Vite + Vitest, which saves a lot of dependency maintenance. I definitely miss Vue 3's VS Code extension when I work in React, though.


So the single most important factor that differentiates front-end frameworks is DOM diffing performance?

Well, IIRC, Elm is faster than all the examples given, and yet front-end developers find Elm to be weird and frightening.

So I’m not sure I agree with the premise of the article.

I also don’t agree that there can be so many valid answers to “Find a change detection paradigm that fits the needs of your application”. Surely everyone just needs the one that is most efficient and least broken.


Don't you find a language without higher-kinded types or type classes frightening, too?


Funny :)

I’m not sure how to answer this question. Any serious answer would spoil the joke.


The way we do SPA is kinda nonsensical because we're fighting the browser. It would be great if we could simply change a value then see the result on the screen at the next tick. DOM is great but not ideal for truly dynamic stuff with lots of things going on, which is pretty much the worst case for the tech.

Really dynamic apps can be really fast with canvas but I am not we have the tools to replace DOM websites just yet. Maybe in a decade.


Haha, the anti angular bias in this is so strong. From getting things just wrong to presenting their own feelings as fact. Makes me discredit the rest.


Surprised nobody, and the article, mentioned RiotJS. It's maybe less framework and more library but, it is easy to make these little web-components. It's also one of those setState and repaint types. My primary drivers is that is easy to bolt into existing projects - so one can gradually add more dynamic features without the mega-rewrite-all-at-once-to-new-tools dance.


The React code in the article has a bug. If you click "Increment later", then immediately click "Decrement", the counter will go from 0, to -1, then erroneously to 1. To fix this, the code for the "Increment later" button should be `setTimeout(() => setCount(count => count + 1), 1000)`.

I don't believe any of the other frameworks have this specific problem.


React is not reactive at all, the "state" management is you calling a function "setState" to re-render the component.

And I find manual render very usefull and once you do it, you can have a global state as simple as a global object, no need to use useState anymore.

https://github.com/dezmou/useRender


This is extremely inadvisable for numerous reasons and goes against the entire design philosophy of React.

In short, injecting a naive render function over the react virtual dom calculation is wildly inefficient.


This piece of code only rerender the component as if you do a setState, in fact it does a setState under the hood, maybe the word "render" is missleading.


The reactive part is every component getting that prop will update versus needing to write a function in jQuery to manually update the inner HTML of those components


States are scoped to a component, how "every" component that get the prop update ?


If you pass a prop to a child component, it will update whenever you change state

If two unrelated components are consuming the same context, it will update when that context updates.

Using Zustand, there's virtually no overhead to this and it's extremely reactive


"If you pass a prop to a child component, it will update whenever you change state"

Well if you update a state, every children of your component is updated as well regardless of if they have a prop passed.

I know there are way to make react trully reactive like Valtio using proxy or maybe Zustand, but my point is that useState or useContext is not "reactive" at all, you must call a function to rerender the components.


If by "Updated" you mean they are looked at in the virtual DOM to determine if they have to update, then yes. But children aren't repainted onto the DOM just because their parent is. This still is Reactive


By Updated I mean that more than comparing virtual DOM to real DOM. - If you have a useEffect without dependendy, it trigger it. - If you have some map, filter, sort, any lambda in your template, it will be executed again. And that is true for your component and every children recursively.


Perhaps adrift a bit of the main topic, but curious if anyone with more than cursory experience with any of the mainstream browser front end frameworks and JavaFX could briefly compare and contrast them.

I do not consider myself a front end person, but I’m slowly getting more adept with FX, more so as a hobbyist than professional, but have no experience with the browser systems.


Worthless article if it doesn't include Web Components or Web Component-based frameworks like Lit. Google, Adobe, Netflix, SpaceX, and many other companies use standards-based components, so it's not some niche thing. Funny that the "framework" that will probably outlast everything in the article isn't even mentioned.


Stop arguing about which framework has a developer experience that you prefer and just use Qwik which is the only one that changes the playing field due to resumability.

You can argue over if you prefer React, Angular, Vue or Svelte but at the end of the day you're still shipping 1-3mb bundles instead of 1kb and resuming


There is only one important factor: the frameworks others wrote, and the ones you wrote yourself.


What about Solid.JS?


Solid works differently in that the output is implemented as a side effect of a subscription to a piece of state. Simplified, think of a signal as an event emitter and a variable with a getter and a setter. Using the getter adds its downstream effects–most notably adding/modifying DOM nodes–as an event listener. Using the setter updates the state and triggers the event listeners.

Most other frameworks here (all? not sure about Vue) will invalidate and rebuild an entire component subtree if a piece of state in them changes. The solid runtime doesn't care about components so changing a piece of state high in a subtree will only update the piece of the DOM directly depending on that state and not any nodes that were authored as sub-components.


I find looking at component level change detection without giving equal space to the store change detection and the integration between the two insufficient.


The best change detection framework is missing from the article. It is obviously Ember.js. It is just so elegant and simple.


Why doesn't anybody pick up elm in these comparisons?


The problem I have with react is that if I set a variable using a hook, it's asynchronous and not immediately available in my code. It's nice for updating the DOM but causes me lots of race conditions.


This is by design, the race conditions are caused by your implementation not complying with the constraints it was designed for you to stay within.

Use a local variable (not react state) if you need to store a value and read it back synchronously. Use a useEffect hook if you need side effects to run when react state changes, and finally useMemo if you need derived react state


This is an issue of you not understanding how to properly manage the state of your application, not React.

It's very likely that you're using effects improperly.


Effects are way too easy to use improperly. I used to be a fan of useEffect when it came out because of its concision, but after working with countless devs/agencies, it's clear that most people misuse it. The rules of useEffect are inelegant, and creating readable state machines with useEffect is very hard. You end up with spaghetti code either in your custom hook, or directly in the component. While most components need 2-3 effects rather than 20, useEffect is not a scalable way of managing state IMO.



Nyeh. The most important factor in a framework is debuggability if the stack trace is messed up and the frames are polluted by a hodge podge of state variables I want nothing to do with it.

The rest is nice to have.


Not a single mention of URLs, statefulness ...


can't forget library support.


I’m not technical enough for this. I thought a front end was about the user.


It is. But the hardest thing about the user is interacting with them. When the user does something, something in the program changes, and you have to change the screen to match. Or updating the screen when some information comes in from outside (like an API call).

That's basically what the article is about: keeping state in synch with the user interface. There are a bunch of different ways to do it. TFA compares a few of them.

A front end has to do a lot of other things, and it's kinda overstating it to say that state is "the single most important factor". But maybe not by much.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: