653 comments

[ 1.8 ms ] story [ 105 ms ] thread
Lots of nuggets of truth in the article. Dev Experience vs User Experience is mentioned.

On my part, I never jumped on vite, because the only argument I've heard for it, is faster dev builds. But my current builds are fast enough (I grew up with 40+ minute compile/link cycles in C++).

And I don't have a week to change my current scaffolding. If you do things right in your company you never have a week to change such a tool. Because that's a week you could spend on relevant features and bugs. Heavily advocating tool changes are fast-track to become a "problem engineer".

Also, yes, React has aged very poorly, and even more so, the ecosystem around it is rotting hard. It's moldy.

But it's still very good and is almost perfect. Maybe instead of writing a new framework, we should consider doing a React 2.0 with batteries included.

> Maybe instead of writing a new framework, we should consider doing a React 2.0 with batteries included.

Besides what's already in NextJS, what are the batteries you're thinking of?

For one, built-in global state management like zustand. Context is too clumsy and iirc, causes unnecessary renders.
Seems like an excessively conservative approach imo. If advocating for tool changes wasn't important sometimes, I don't think it would be relevant to cite some much slower process.

Sometimes the tools suck, and sometimes you need to improve that situation, ideally in a gradual fashion. Sometimes they're passable, and there are other priorities, but usually tools start showing their rust eventually.

I also think it's extreme to say it's aged "very" poorly. It provides a relatively productive way to express UI as composable state machines, and now it just has a solid ecosystem of viable competitors.

I strongly agree that the industry often prioritises DX over UX but

> I never jumped on vite, because the only argument I've heard for it, is faster dev builds

I wouldn’t assume Vite is worse than its competitors for UX just because it doesn’t explicitly say it’s better

If a 30 second build can be cut to 15 seconds, for 20 engineers, assuming a conservative 10 save-file commands per workday (HMR!): this change converts ~216 engineer-hours per year from non-productive to potentially-productive. Given an average 40 hour work week, you could task one engineer on this change for five weeks and still come out ahead.

This is the calculus. Vite isn't built for the "lone ex-C++ dev" shop. Its built for teams that can run this calculus and realize that, 80% of the time, investments in speeding up the software development lifecycle are among the highest leverage, most direct correlates to productive output a software shop can make.

I completely agree. On that note, giving devs maxed out computers also save time and money on the long run, most companies still don't do it.
>Hooks are undeniably great

I recall seeing far more negative sentiment than positive over the years re. hooks. Maybe it's just me.

Maybe not hooks in general, but useEffect hooks seem to get hate these days. Even though they were the recommended way just about a year ago?
Hooks are like cooking with really sharp knives - excellent when treated with care and healthy respect for the dangers, but will/can cut you up really badly in a moment of absent-mindedness.
Yes, I've seen this firsthand. Side effects can put you in a huge mess, i.e. an effect that triggers another effect, that triggers another effect... and it can be hard to follow that logic if you're unfamiliar with the codebase. I do think it causes confusion compared to the 'setState' class-based components because I can follow all of the setState calls very easily.
That's a great analogy and actually gets to my main beef with the whole "I can ship faster with react" argument.

When using a sharp knife it's even more important to stick to the principle that slow is steady and steady is fast. It's the same thing with react, and really any of these frontend frameworks that keep piling on complexity. Sure you can ship features fast and if it's throwaway code the great, but if you ever want to reuse or grow it that quick code is a nightmare of tech debt.

I think it is more negative towards centralized store and using it for every possible project being ridiculous since hype died down recently.
The amount of negative vs positive sentiment you see can be misleading. My sense is that a significant minority just don’t get on with them and make a lot of noise about them because the ecosystem sucks now if you don’t like hooks. While that the majority think hooks are somewhere between useful and great, and don’t really comment much on them.
Indeed, it's selection bias. You're only seeing/hearing those who talk about it, and those who talk the most about it are (probably) from opposing ends of the "positive/negative appreciation of it" scale.
they're a mess. what the react developers really wanted is a DSL[0], but they couldn't sell that as well so they hacked hooks into JS.

The whole things about "rules of hooks" should tip people off that it's sketchy. I mean, have you ever actually thought about how:

    function Component() {
        const [state, setState] = useState(1);

        return <>{state}</>
    }
actually works? How does `useState` know what bit of state to grab? (it's a global store identified by the function you're in, and an index. it's why you can't change the order of hooks nor call them conditionally).

Hooks are literally javascript in the sense that they're functions you can call in javascript, but they don't operate under any of the rules people actually think of when they write javascript. If for any `function x() {}`, `if (condition) { x() }` was a fatal error, people would consider that terrible API design and the function shouldn't need to care whether it's called conditionally.

Sure, react hooks 'compose' better than class components, but they also don't compose *at all* with anyones mental model of how JS works. What they needed was different syntax or a DSL in general (what svelte does is a lot more understandable, imo).

[0]: jsx is a DSL but it's merely one bit of syntax sugar. they needed a hell of a lot more.

useState is basically a local [observable] variable slot in a closure, but defined at runtime in React. That’s why it cannot be conditional. React is an ugly attempt to pretend you’re in a powerful functional language while it’s javascript and you run its core manually and follow the rules for “…reasons”.
I still don't understand what the problem is with class components. They made sense. Now I'm suddenly relying on the behind-the-scenes magic of useState and useEffect.

useEffect in particular really messes up my understanding of the flow. Lots of bits of code that may or may not be executed based on the dependency array, and that means my component function is going to be executed several times, each time just to execute another useEffect. It works, but it's not a programming model I like.

shouldComponentUpdate() also doesn't exist in the function-based components. Instead there's a few different hacks to get the same effect.
Class components don't scale or compose well. You have a fixed number of lifecycle methods, and each new behavior you add to your component has to be split up across those methods and mixed in with everything else. This means that your components are going to get much harder to debug and maintain as they get larger, and it's very difficult to split out common behavior in a form that's easy to mix in to existing components. That's the advantage of hooks: each hook can be defined by a single function that manages its entire lifecycle in isolation, and can be dropped into any component with a single line.

> that means my component function is going to be executed several times

Who cares? That shouldn't be your concern. All you should think is "this effect will get called every time these values change. Am I okay with that?" React will handle running your component function consistently with the right state, you just have to worry about whether your component is rendering the right thing given that state, and running the right effects. React handles correctness. You don't have to remember "oh whenever I update this state, I have to run this other function to make sure everything is in sync" as with class-based components. Because invariably, another engineer will come along, make some changes, and forget to call that function. Now you have a really tricky bug to find that depends on internal state.

This is why hooks have caught on: you can truly write pure declarative components (most of the time) and not worry about execution order or dependencies at all. The tradeoff is that you have to think very...reactively. It's definitely a different mindset, but it means that there's a whole class of bugs you never have to think about because React handles it.

Vue's equivalent (Composition API) handled the hooks concept so much better IMO, by just eliminating 99.95% of the footguns present with Hooks. Obviously Evan had the benefit of hindsight to work with, but still, why people would opt to torture themselves with React when Vue exists will always be an enigma for me
I’m old enough to remember pointing out all the ways that React sucked when it was new. It’s unsurprising and profoundly depressing that it’s losing its grip only after so much damage has been done.
"I disliked the popular thing before it was popular" is not an achievement to take pride in. Heck, most people who picked up React disliked it before using it.

The article states that there are better options now so continuing to use React is not the best choice. This was predicted by most advocates of React fairly early on. The mantra was always: React will be replaced by something better; for the time being that something is still React (i.e. present day React looks very different from the early class-based React).

At the time React started gaining momentum it was simply the best option for what it did. The biggest alternatives were AngularJS (which was known to be a dead end), Angular 2 (which was stuck in Google's cycle of "let's rewrite everything several times because we're not dogfooding this" -- remember that Angular 2 was initially based on `Object.observe`) or the old rusty toolbelt of jQuery, Backbone, Knockout and whatever else was still polluting globals at the time. React eliminated entire categories of bugs at the time and was the first major frontend framework (well, library) to also support server-side rendering.

I think good arguments can be made that e.g. lit-html or htmx is superior to JSX. But at the time those didn't exist and the reason people disliked JSX was that it was different and that it looked like something many had tried before (i.e. VDOMs and XML-ish template DSLs) and that had always ended up sucking. That criticism however came from a place of ignorance (literally "not understanding how it actually works and what it is") and prejudice (i.e. judging by superficial first impressions, not the actual implementation and semantics).

If you think React has uniquely done "so much damage", you either think frontend frameworks in general are inherently bad (which is an entirely valid opinion to have tho I wonder why you think seeking out articles about things you hate just to vent about them is a productive use of anyone's time) or you're being extremely disingenuous with regard to what came before React and what can be considered "React's fault".

I’ll wait for the “Svelte kind of sucks, and plain old React is fine” article next year before making any decisions (well really I’m waiting for the ‘jquery can do what React and Svelte do believe it or not’ article still).

Where are my AI JS blog post generators at?

Unless … No can’t be … There’s no way these articles have been AI generated this whole time … right? RIGHT?

I love your comment for the quote "old rusty toolbelt of jQuery, Backbone, Knockout and whatever else was still polluting globals at the time"! In all seriousness though, it might be hard to understand how React felt when it was young for those who missed it or have forgotten. Like with any new technology, those writing about it were involved in its development or early libraries based on React, which improved the signal to noise ratio of the ecosystem considerably. It is not only strictly React that was special, either - with the necessity to 'transpile' React's JSX to ordinary JavaScript came the excuse to use a proper build system, rather than just copying JQuery files into each other, which was the predominant distribution method at the time! Of course, eventually, the build systems themselves were overcomplicated and messy, but at that early stage things like SASS and JSX were very exotic and clever.
> Of course, eventually, the build systems themselves were overcomplicated and messy

True. On the other hand people are now rapidly waking up to the fact that the promised simplicity of having real module systems running in the browser (and ideally federated at that) introduces a ton of complexity (not to mention performance problems) compared to simply having a bundler even when you have to deal with code splitting.

It's also easy to miss how rapidly React influenced the space. Because React was so narrowly defined (i.e. it didn't bring any state management or styling - I remember it being called "just the V in MVC") a ton of other libraries sprung up around it and it even led to a short rise in popularity of otherwise very niche reactive functional programming DSLs and arguably helped sparking interest in reactive programming in general, such as RxJS (which has since seen and ebb and flow of popularity in Angular).

What was bad about React then is the same thing that’s bad about it today: despite the atomic unit of React being a “component”, the best practice was always to re-write your entire fronted in JavaScript, shipping a blank HTML page and forcing the end user to download a megabyte of JavaScript to make their computer write the HTML, for some reason.

And in order to get the “convenience” upgrade over jQuery+backbone or whatever, you had to abandon all existing standards and best practices, making any JavaScript you were using that was not in the style of React useless.

Web components that encapsulate state and interactivity, usable alongside a regular HTML page, were an emerging direction when React shipped and forced everyone to re-write ‘class=‘ to ‘className=‘. And being forced to re-write every app as an SPA was a dumb idea the day people started doing it, even in light of other available approaches.

You’re right that a lot of people hated React before they ever tried it, and they were right, even with the full knowledge of what it was replacing.

> Web components that encapsulate state and interactivity, usable alongside a regular HTML page, were an emerging direction

They weren't. By 2013 web components were still flailing around completely ignoring everyting that was happening in the world and coming up with an unusable and badly design API while crippling a bunch of other things in the process (like forcing C++-like OOP onto prototype-based Javascript)

> when React shipped and forced everyone to re-write ‘class=‘ to ‘className=‘.

React is basically just Javascript. What do you think className is? https://developer.mozilla.org/en-US/docs/Web/API/Element/cla...

> And being forced to re-write every app as an SPA was a dumb idea the day people started doing it, even in light of other available approaches.

What do you think web components are if not a way for people to dump a bunch of Javascript onto a page (and dozens of Javascript-only specs to accompany them)?

(comment deleted)
> What do you think web components are if not a way for people to dump a bunch of Javascript onto a page (and dozens of Javascript-only specs to accompany them)?

You can get most of what you need onto a web page with HTML. Some components need to be interactive. For that, you need JavaScript. The question is, do you:

1. Keep the rest of the HTML in place, and add JavaScript to that HTML for the interactivity you need (the web components approach)

Or,

2. Re-write the entire web page as a JavaScript “app”, including the parts that are just fine as static HTML, for the “convenience” of the pieces that need interactivity (the React approach)

Both involve “dumping JavaScript onto a page,” sure, but they are totally different approaches.

For a decade, the entire industry picked “2”, and now the internet is a bloated disaster.

> Keep the rest of the HTML in place, and add JavaScript to that HTML for the interactivity you need (the web components approach)

It's not the web components approach. Almost nothing about web components is about progressive enhancement.

> Re-write the entire web page as a JavaScript “app”, including the parts that are just fine as static HTML

That is literally what web components are. They can't even participate in forms without Javascript

You're describing AngularJS, not React. When React hit the scene there were services for "pre-rendering" your AngularJS app for SEO and crawlers. "Rehydration" for server-side rendering was one of the biggest selling points of React.

Heck, even literal Web Components were stuck in that direction. The big reveal of the Web Components implementation in Polymer involved a demo page that was literally an empty HTML page with a single <DemoApp /> custom element node and a massive JS runtime. And that was after React had become popular enough for Googlers to actively heckle React and other libraries on Twitter: #UseThePlatform.

React of course lapsed in server-side rendering eventually because it was not a priority to Facebook and therefore only an afterthought. Arguably the killing blow was Create React App not giving any thought to it at all. But one of the most popular React frameworks is Next, which is now more than 6 years old and is best known for being good at SSR.

The class vs className thing is a silly argument and enough articles have been writing about this that I don't need to reiterate the full argument here but basically the DOM API calls it className (because it wanted to avoid reserved keywords in property names) and most libraries have special-cased "class" to be an alias for "className" or even go out of their way to prevent you from using the actual property name. React actually does less here, not more. The rendered DOM itself (i.e. the HTML sent over the wire in SSR or what you see in your DOM explorer) is no different whether you use JSX where it's called className or a template language that translates "class" to "className" for you.

I think the people expressing negativity about hooks are a small minority. Hooks are a massive step up from class component lifetime methods, and the composability of hooks can lead to some very clean and powerful code if you know what you're doing. We're using hooks at work, with rules-of-hooks linting, and I can't remember the last time we had an issue or bug because of hook semantics
I think they're a bit of a mixed blessing. The way they can decouple business logic from view logic is absolutely fantastic. But having to wrap everything in useCallback and useMemo to avoid re-renders is a bit of a footgun and a step backwards from class components.
My understanding is you don’t usually need useMemo and useCallback, unless you’re seeing performance issues and your profiler is pointing at un-memoized code as the culprit.
We wrap very little in those methods and things are just fine.
Best practice in general is not to use either of those hooks unless you notice performance issues, or have a good reason to, React is optimized pretty well and unless there are serious numbers of rerenders happening they aren't noticeable.

Josh Comeau has a nice overview of useMemo and useCallback, one of my favourites: https://www.joshwcomeau.com/react/usememo-and-usecallback/#w...

The problem is that they don't work to fix performance issues unless they're implemented all the way up the tree. So if you have a perf-sensitive view, then you'd better hope that your "application frame" components (or anything higher than the perf-sensitive component in the tree) are optimised to avoid re-renders. And it's a massive pain to retrofit this in later if you haven't been strict with it from the start.
Hooks are a step in the right direction, I just have a feeling that having to wrap in useCallback and useMemo and having to add dependencies manually can't be the final step of web app development. I look into the author's suggestions for the next generation of hook api in other libraries. However, I still don't want to rely on those libraries in large long living projects.
> The way they can decouple business logic from view logic is absolutely fantastic.

It’s a great idea to decouple business logic from view logic, but then hooks are the wrong place to start. Last time I checked, you couldn’t use them at all outside of a React component. That’s just a terrible place to put your business logic if you want it to be isolated from the specifics of your view.

I’ve mostly moved away from React, so maybe this has changed recently, but it seems difficult considering the fundamentals of their design.

The problem that hooks solve really nicely is when you want per-component state, but you duplicated versions of that state (and surrounding logic) in different components. With class-based React components you had to use things like higher order components, or manually add the state properties into each component. With hooks this is all neatly abstracted away.

You don't always need this. Sometimes your business logic can just be a pure function. But where you do (and IME this is quite common), hooks are super nice.

The React team is testing out a compiler time approach to useMemo instead.
Like others I disagree that you have to wrap _everything_ in useCallback/useMemo.

However saying you only need those for performance reasons is wrong.

There are cases where avoiding re-rendering (thanks to useCallback) is avoiding an infinite loop.

I created a codesandbox[1] to illustrate this. Wrapping the "reset" function in a useCallback solves the infinite loop.

If your initial reaction is: "you should not create logic like this" or "you're adding unnecessary stuff" please note that this is a stripped down version of a real use case, where things make more sense.

The point is that it's hard to come up with a rule as to when to use useCallback. The best I can think about is: "if you don't have direct vision on where your function will be consumed, wrap it in useCallback". For example, when your function is defined and returned in a hook or defined in a parent component and given to children.

The point is that any of those children/consumers could have this function in a useEffect and so list it as a dependency of this useEffect.

[1]: Warning, clicking "Start" creates an infinite loop in your browser. https://codesandbox.io/s/intelligent-rgb-6nfrt3

This example doesn't resemble anything someone would actually write though.

There's no reason to reset before setting data again, and I'm not sure why you'd even consider putting the functions in the dependency array for the useEffect in usePets.

I can imagine reasons you'd want to setData in a useEffect (maybe when something else changes, or the user performs some interaction, you fetch new data and then set it), but the dependency would be the thing that indicates that action has happened, and not a reset function returned by a custom hook coupled with your data and setter function.

I tried to address your type of reaction in my "If your initial reaction is(...)" sentence, but that failed.

The point is not that this code is good or bad. It's that it is possible to write such hard to predict code.

Remember this is a stripped down version of some code running in production.

Now to address your specific points anyway: "There's no reason to reset again": in the non-stripped down version, the hooks rely on a third hook (let's call it "useToken") and needs to react accordingly to this change of token to fetch new data

"I'm not sure why you'd even consider putting the functions in the dependency array for the useEffect in usePets.": unfortunately react-hooks/exhaustive-deps is here to warn you. You can disable or ignore it but I guess you expose yourself to a real missing dependency? Genuinely very keen to hear if you use this rule in your projects and what you do in such cases (where you use a function in the useEffect but do not want to re-run the effect each time this fn changes). To me it's such a weird/unnatural thing to list functions as dependencies because almost all the time functions do not change.

Ahh well the setter functions are excepted for the exhausted-deps rule, which is why I hadn't encountered it: https://legacy.reactjs.org/docs/hooks-faq.html#is-it-safe-to...

> (The identity of the setCount function is guaranteed to be stable so it’s safe to omit.)

Maybe in JS it still warns in your example because setData is passed in from calling useResource, but at least with Typescript I'm pretty sure eslint infers this

I sometimes check React discussions and it's full of new made-up terms about managing issues that are uniquely caused by React or some previous iteration of a React technique. What happened with the simplicity of just "generate some boring ass HTML DOM in JSX, and it applies the diff to the actual DOM". That's it. That's the entire value proposition of React and it needs no hooks, handles, states, immutables, events, data trees, properties, arguments, components, nothing else. Oddly that still works just fine, but no one uses React this way anymore.
Only if you're working on super simple projects. Or don't want reusable code.

How do you share data fetching logic across components

How do you share a common UI functionality like toggling states, starting timers, across components?

People are now more concerned about clearly writing business logic while stiching together React lifecycles methods, instead of the other way.

Lots of assumptions. And they're all based on the fact React must be some all-encompassing framework, like all other frameworks are or have become, and there's no other way to organize your app, possibly, unless it's dropped from above, from the framework, and is intricately interwoven and coupled with it.

But there's another option: don't use React as a framework, use it as a library. And do your modules/components/reuse/fetching in your own plain code, while using React as a display layer. You don't even have to use React for the entire UI.

(comment deleted)
> Lots of assumptions. And they're all based on the fact React must be some all-encompassing framework, like all other frameworks are or have become, and there's no other way to organize your app, possibly, unless it's dropped from above, from the framework, and is intricately interwoven and coupled with it.

This is a silly argument to make. People don't want to onboard onto a myriad of little reinvented wheels just to put together a GUI. This is well known to be a major mistake, and the root cause of failure of countless projects.

People are paid to deliver features and fix bugs, and not to peruse through npm like they are playing Pokemon. React solves their problem by solving developers' problems. Don't you understand the value of using a standardized tool that answers all your problems?

People are paid to deliver features and yet the forums are full of why this and that is annoying in React and how version X.Y.Z is replacing it with something else that will be the next annoying this that people in the forums will discuss. I sense lack of alignment between intention and results here.

Knowing how to architect your app remains important, and once you know it, it's trivial. For those who refuse to learn it because they deem it a gargantuan task, frameworks will keep trying to provide a "generic" solution, but a "generic" solution is by necessity overcomplicated, overengineered, and bloated, because it's trying to address EVERYONE's concern and specific needs.

At no point in time will a monolithic "everything solution" be simpler than a specific modular solution to a specific modular problem.

React started with the promise of simplicity, and being a library, not a framework. Somewhere along the line this ideal was lost, this happens often. And it happens often because many small elegant solutions start with the intent of replacing the status quo bloated behemoth framework that everyone hates. What they don't realize is that with lack of understanding of why frameworks end up like this, this is the fate THEY will share one day. Entropy is a b**ch.

> People are paid to deliver features and yet the forums are full of why this and that is annoying in React (...)

Online forums dedicated to frameworks are filled with questions on what the framework does and how it works. No news there. What did you expect?

> Knowing how to architect your app remains important, and once you know it, it's trivial. For those who refuse to learn it because they deem it a gargantuan task, frameworks will keep trying to provide a "generic" solution, but a "generic" solution is by necessity overcomplicated, overengineered, and bloated, because it's trying to address EVERYONE's concern and specific needs.

I'm sorry, you typed a lot of words to actually say nothing. So you don't use all features provided by a tech stack. So what? That is not bloat. Writing software is not a game of bingo.

Also, reinventing the wheel when a fully working and tested wheel is already available is a very dumb mistake and a fundamental failure in the decision-making process.

> React started with the promise of simplicity, and being a library, not a framework. Somewhere along the line this ideal was lost, this happens often.

I don't think this is remotely true in any way. Undoubtedly React greatly simplifies the work of putting together working and full-featured SPAs. The concept is so good and so extensively proven that React is already being used to develop native GUIs in desktop applications.

Your comments reads as if you're tilting at a React windmill. It's ok if you like vanilla JavaScript, but you're not being honest with yourself if you're failing to understand what problems React solves, how well Reacts solves them, and why the whole world has basically standardized around it.

> annoying in React and how version X.Y.Z is replacing it with something else that will be the next annoying

Interestingly enough, React is probably the only major frontend framework that is obsessive about backward compatibility.

I had to pull a component from an internal project into my codebase recently. The component was still class-based. Worked without a hitch in the modern hooks-only code base.

> I sometimes check React discussions and it's full of new made-up terms about managing issues that are uniquely caused by React or some previous iteration of a React technique.

In other words, you're checking React discussions and finding discussions on how to maintain React code. What were you expecting to find?

> What happened with the simplicity of just "generate some boring ass HTML DOM in JSX, and it applies the diff to the actual DOM".

React happened, which does just that but transparently and effortlessly. In fact, React does it so well that a concern is to prevent it from applying those diffs when being updated when it doesn't need to.

> That's it. That's the entire value proposition of React and it needs no hooks, handles, states, immutables, events, data trees, properties, arguments, components, nothing else.

React does not need those features if you are using React for things other than developing graphical user interfaces, which by their very nature are stateful, emit and react to events, handle properties, etc.

React went from class components to function components and from lifetime callbacks to hooks. These concepts are completely orthogonal.

There exist design choices that would end up with a different combo. Class components with hooks or function components using lifetime callbacks.

Yes, hooks — basically sub components with possible effects and yielding values, or DOM output — are a terrific idea and much more functional and principled than explicit lifetime callbacks.

All the dependency arrays and non-conditional checks etc are because of the function components approach and don’t have much to do with hooks.

I feel nearly every discussion on hooks mixes this up and makes the whole conversation just weird and confusing.

> All the dependency arrays and non-conditional checks etc are because of the function components approach

And because of React's design choices. Reactivity can be achieved without explicit dependency lists.

no one ever got fired for choosing react though. that chart library? already exists. mobile? react native. some weird combo box autocomplete ui component? someone already wrote that, just reuse it. dev tooling with debugging and profiling and all the memes. new hires know where to start.

ecosystem is king

People & thinking like this makes frontend development more complicated than it already is. I refuse to work with people who has this thought, had to make my HN comment ;) I think automated acceptance testing in browser is king rather than unit tests on the frontend.
> I refuse to work with people who has this thought

So you refuse to work with people who do not want to re-invent the wheel for a shiny new framework because it is "better"?

the problem is that every framework comes with only three of the wheels you need, but in each framework the missing wheel is different, so you will always end up "reinventing" a wheel that another framework already has, because if you switch to that other framework, you'll have to reinvent a different wheel instead.

if you don't want to reinvent a feature that another framework has a solution for, then i'll ask you to show that this other framework solves more problems than the current one, and it's worth the cost of switching (or switching back).

but, like you, i don't like to switch to a new framework just because it promises to be better.

i prefer to avoid switching frameworks as long as i can. i'll only switch in a project when i run into a problem that the current framework can't solve at all.

This. People who disagree - why not write in Haskell?
One of these days an LLM will translate ecosystems to less popular platforms. Or at least it will write the glue logic.
Isn’t the irony of this that LLMs will only have enough training data to generate meaningful code for popular frameworks?

Less popular platforms will get more isolated rather than less as the wave of generated code that “works out the box” only works with stuff like react…

If I thought I could deliver a better product and faster with Haskell, I'd use it.
People suggest adding other languages to browsers all the time. Why not - because then you get bombarded with thousands of “why you need that”.
We write in Elm.

To elaborate further: I don't believe ecosystem is king. Success can mean different things to different people, and is not always a single dimension of "popularity units".

We have one Elm project at work, and frankly it is a nightmare because no one wants to take those tickets and everyone avoids it like it is radioactive. Personally I have had to do quite a few of them and every time I find it worse compared to React, people just seem to hate it and it is very difficult to find people who want to work with it, so that project is now being rewritten in, guess what, react.
Frankly, I've worked with a couple React projects that are exactly like you describe, too.
The key difference being the team has 20 people that are fluent in React, vs the guy that decided to write it in Elm and left last year.
I've seen those React projects get rewritten.
I’ve had to deal with a nightmare Elm project. The difference is, I know what I’m doing. I solved all the problems, created examples to lead other devs and everything was fine. Have your company contact me if you need help.
i could say the exact same thing as a React dev.

> I’ve had to deal with a nightmare Elm project. The difference is, I know what I’m doing. I solved all the problems, created examples to lead other devs and everything was fine. Have your company contact me if you need help.

I’ve had to deal with a nightmare React project. The difference is, I know what I’m doing. I solved all the problems, created examples to lead other devs and everything was fine. Have your company contact me if you need help.

ghcjs is not production ready imho

shipping as wasm is not an option as it increase bundle size for very little gain (unless you're running something super performance intensive).

We would need a bridge between sane languages and a JS framework, not just JS in general.

5 years ago I would have done a Haskell -> hyperscript bridge to build frontend applications. Today I would do a Rust -> Solid.js bridge to build frontend applications.

Logic could be compiled down to JS, UI could be compiled to Solid.js components.

Many reasons not to use Haskell, not just the ecosystem.

Source: 7 years of Haskell in production

Javascript is the ecosystem. The majority of those react libraries are thin wrappers on vanilla javascript libraries.
They would have. In 2014 when React was new, the obvious safe choices were Underscore or Angular. Maybe Ember if you were clever. React would have been new and required a lot of justification.

Likewise Svelte is mature and well understood enough that like React, enough nobody could get fired for choosing it either.

> ecosystem

If only the article had dedicated a section to how React trained us that things need to be built specifically for a certain framework, and how no other modern frontend framework is as stubbornly incompatible with the platform as React is.

You can use a “meta” framework like Astro if you want, but micro front ends sound like hell. Nobody has really achieved portable non trivial components in a way that doesn’t come with a steep price tag, and web components have never taken off.
IDK, I think Vue is pretty incompatible with the rest of the JS/TS platform — e.g. its templating language is pretty custom, and components built for it won't work in say, Svelte. Same for most frameworks; Solid isn't more compatible, it's just different (arguably better? With the drawback of a smaller ecosystem, because it's younger, and because no one ever got fired for choosing React). Svelte has its own unique lifecycle system; etc etc.

This isn't really any different from frameworks in general: JQuery had its own massive ecosystem that assumed JQuery was being used; trying to use something from the Rails ecosystem in Padrino was unlikely to work well; Vim vs Emacs; etc etc. Frameworks accumulate ecosystems around them and have since pretty much the beginning of programming — I don't think React has nefariously trained ecosystems into the community; I think people tend to build libraries on top of other libraries they know, and React is popular, so it's the basis for more libraries (the ecosystem).

Well, I work for a Fortune 100 company and our standard is Vue, so, yes, using React could potentially get me fired ;-)
You have posted this reply in multiple threads on this post. Is it really that clever?
> no one ever got fired for choosing react though.

Probably because they had already moved on to do the same thing at some other company for more money.

So in mid-2023, if not React, what would HN choose?
for a small project i would choose vanilla. vanilla is good enough today, tons of features supported on all the browsers, etc etc

but for a large project still react.

ehhhh. I'd think maybe lit-element if you're going to be leaning on Web Components to smooth some of the edges of "vanilla".
Lit.dev has been nothing but great for me. Strong recommendation.
I used on a project 6~mo ago and struggled quite a bit to get some basic things to work. Iirc it was related to event handling on nested elements in a component. I believe I felt quite forced to make lots of very tiny components, which made the "try it out" phase quite frustrating.

Do you have any recommended learning resources on building with Lit?

At work, whatever the FE team decides upon, React/Nextjs, Angular and Vue are the only relevant ones.

Privately, I always go with vanilajs.

Mithril.js, for both work and personal projects. There hasn't been a major release since I started using it ~4 years ago, which is lovely in the otherwise churning sea of frontend development.
Solid as a React alternative, and Astro as a Next.js alternative. SolidStart is pretty cool, but I like the flexibility of Astro
If it's an internal app, server rendered HTML and maybe a JS library to render charts. Django or Rails both fit the bill.
Genuine question from someone who's only worked in CSS-in-JS or SCSS preprocessors for the past ten years -- what would you choose for styling?
CSS? In all seriousness though Tailwind or SCSS.
I'm curious if anyone else is advocating for HTMX as a mechanism to simplify bloated front end code bases
I’ve been using it to great effect for the last year or so. It really is a breath of fresh air. In my case, I’m using a Django backend, but there are lots of successful folks using other backend stacks as well so choose what you know best.

The book is a really quick and easy read. It’s pretty eye opening how productive, and effective, htmx can be with so little magic.

https://hypermedia.systems/

i like aurelia because it has a minimal footprint on my code.

i set up routing with the views available and i write a class for each view. there is practically no boilderplate code. (eg what is $scope in angular is simply the class object itself in aurelia)

any class level function and variable is automatically made accessible from the html template. classes and templates are linked by sharing the same name: welcome.html, welcome.js, export class Welcome ...

while in angular you have to declare everything explicitly, aurelia mostly just figures it out by itself.

I'm seriously looking into Preact[1] as a library which is philosophically aligned with React, fits in nicely with the rest of the JavaScript ecosystem, but has a very small footprint for what it provides (3KiB).

[1]: https://preactjs.com/

Maybe it's changed with more recent version but that small footprint was a trap for us. We picked it for that, and ran into weird edge cases / bugs in preact projects using third party libs that didn't occur with react.
Thanks for the warning, although in my case I'm not planning to use any third-party libraries except maybe some fundamental ones like Hammer. :)
Some niche ass framework with little to no eco-system. Or plain javascript by people thinking the web is used to display a single page worth of content.
If you don't already have a backend framework, I see no reason to disagree with TFA's recommendation of Svelte/SvelteKit[0]. It's simple, there's no runtime framework magic to debug, everything works exactly like you assume it does, it's pretty darn fast, and the DSL syntax isn't easily confused for the HTML it's templating (looking at you, Vue).

If you do have a backend framework, then having the backend drive the dynamic updating is a pretty great way to go, so HTMX[1] is the least obtuse way of putting the most power in the hands of the backend. Really makes 'a light sprinkling of JS for interactivity' closer to reality than vanilla JS allows, without adding cognitive overhead.

[0]: https://svelte.dev/ [1]: https://htmx.org/

PHP with server side rendered templates.
If I had to choose a frontend framework right now, I'd probably go with Svelte. That was my opinion before I read this article, and this article confirms it.
Not sure, the typical HN user has JS disabled.
(comment deleted)
> There was a time, several years ago, when React was pretty much the only game in town when it came to server-rendered content

This is actual nonsense. Creation of a fully-formed HTML document server-side is performed by countless platform combinations.

It is a hilariously naive statement. Everyone’s PHP CGI bin on their basic free web hosting did/does SSR. Every Rails and Django app does SSR. SSR has been norm since forever.
PHP CGI, Rails, and Django can server-render a client app that gets rehydrated on the client? Because that's what the blog post is talking about.

Let's be a bit more charitable when we pull quotes out of context.

What does rehydrated actually mean? Honest question.

In PHP the server computes the full HTML and sends it to the client browser. What would need to be added here to have “client side hydration” for example?

They mean not having to write any client-side JS. So, having a mechanism to use the backend language for both the content that gets displayed when the page/component first loads AND any interactivity thereafter. It’s easy to populate a <select> with values with (traditional) SSR, but then you’d often have to write some JS to make additional requests when the user makes a selection, for example.

Seems weird to me to use “SSR” as a term for both SSR + hydration, but I’m not in the web dev lingo bubble so what do I know? The author doesn’t use the term “hydration” until the blurb at the end talking about Quik

The whole blog post is about React and client-side development, so the context should be clear. If it weren't clear, the other SSR-capable frameworks they list in the SSR section are client-side frameworks with the same SSR feature.

SSR when talking about React is very clear jargon.

Let's start in the other direction.

You have a client-side web application. You want the initial-state HTML to come over the wire on the first response rather than wait for the client application to load, make a request, and then render the result.

So you use a tool like those listed in TFA (like Next.js) to run your front-end app logic on the server to generate the HTML and send it to your client where your front-end app mounts to that HTML and takes over (cutely named rehydration).

This is relatively cutting edge stuff which is why it's weird to assume OP is an idiot who never knew about PHP or CGI when he wrote an article that is obviously talking about client-side applications and rendering them on the server.

Not a front-end dev, but I think (correct me if I'm wrong) the usual way of SSR were things like returning a plain HTML web page in Spring/ASP.NET/whatever and that the main invention of Next as far as SSR goes is that you can write your server's HTML docoment in JSX.

So I would say the author's point (if I was asked about it) would be that these other new front-end frameworks support similar things, instead of sending plain HTML over the wire.

Am I wrong?

(comment deleted)
Server-side rendering doesn't actually mean server-side rendering though. It's a term of art for a paradigm where applications are written in a single code base, and certain elements are rendered on the server and cached and other, more dynamic ones are rendered by the client.

The older platforms for this had you essentially write two separate backends - the one which renders HTML, and the other which returns a JSON or XML API for your hand-written JavaScript (or CoffeeScript or JQuery etc.) embedded in your HTML.

(comment deleted)
We had that discussion here in the past, I’ll just repeat my point of view: When someone says “SSR” today, especially with a Javascript context, they are specifically referring to Javascript SPAs (pre-)rendered server-side.

So, considering this, is it still nonsense? (I honestly don’t know.)

I don’t care that React isn’t new or innovative anymore, that’s a good thing in my book. Give me old and boring any day.
I don’t know if you actually read the article or not but just to be clear that was not the argument that was made.

It’s not just “not new” it’s considered to be actively worse than all of its peers in terms of things like performance and complexity, it’s actively a very questionable choice in a greenfield project in 2023.

I did, and the fixation on React’s age struck me. Was it the core premise? No. Was it a central theme? Yes.
I agree with you, there was definitely a fixation. The implication is that React doesn't do things the new and better way just because it's old, rather than a deliberate decision.

Goes back to writing SQL

No, the core theme was technical debt. Technical debt happens to be (typically) a function of time.
Claims that hooks are the baseline and then proceed to explain how they don't understand the dependency array in useEffect huh?

It's fine admitting that hooks are a bit too complex. (don't get stockholm syndromed)

(comment deleted)
Right lol I don't even disagree with the author's larger point, but why is this such a common thing to outright admit if you're writing specifically to the topic?

>I’m still not exactly sure what the difference between useMemo and useCallback is

A well put together article.

I still remember the day when angularjs was a new thing(around 2015 perhaps) and as a newcomer I was sucked into it. Just loved the thing. A year later, there were news about Observable JavaScript objects. So objects could send a notification about themselves having been changed. And when do so, do DOM changes could be done accordingly, without having to implement a render algorithm and having to compare shadow nodes with new render results to see what changed every time any data change occurs. I don't think we are past that with React, and as the author mentioned there should be absolutely no reason for a developer to worry about rendering performance (the amount you have to use react, with for example useMemo, useCallback) and is something to be looked after and questioned.

Another argument of mine[0] would be the definitions that have been introduced by react. Component, state, hooks. It seems like we have forgotten about what they are actually called, and its function is in the context of programming (functions, variables, events, etc.). And so people become solely a React developer and can't really see a way out. (from the article: "And maybe—just maybe— your current satisfaction comes, at least a little bit, from simply not knowing what you’re missing.")

[0] - https://medium.com/@ngergo/describing-how-react-works-in-com...

> there should be absolutely no reason for a developer to worry about rendering performance

i have a csv with one million rows. should i load the entire thing into memory and render 1m * column_count dom elements?

> Another argument of mine[0] would be the definitions that have been introduced by react. Component, state, hooks. It seems like we have forgotten about what they are actually called, and its function is in the context of programming (functions, variables, events, etc.).

a Component is a Component because it represents a node in the React tree. not all functions are Components.

state is state because it's more than a variable - updating it triggers re-rendering. a normal JS variable does not come with this reactivity.

hooks are not events. they are wrappers for reusable logic.

It's always the same. Off with the old. Let's use the new. Or new-ish.

I thought we, as in the community, learned a while ago that was a bad path to go down.

That's why there's a resurgence of old-school (yeah, I know...) frameworks like Rails. Or why things like Laravel and the PHP world keep thriving.

Well, React is old now. We know. It's the Rails of the JS world. And it's plenty good enough.

If you know JS/TS and are comfy in that, just use React. Don't feel bad for not using whatever shiny new thing is trendy now. Who cares.

No one ever got fired for choosing react, as some other commenter said.

> I thought we, as in the community, learned a while ago

If I’ve learned anything in my twenty years programming, it’s that the development community never learns anything for more than about twenty minutes. There’s too much turnover without a great mechanism for handing down folklore and war stories.

This.

I’ve been think about this a lot. Like how htmlx is reinventing something called pjax (though better). There’s many other examples. It’s a lot of newer programmers never investigating the “lore”. I don’t think it’s part of CS curriculum, and boot camps certainly don’t teach it. Forget how things they are using works. It’s woefully common for js devs to not understand how variable referencing works. (Which is super important for reactivity)

To be fair, react was badly conceived and designed from the start and has aged poorly since.

I’m all for not moving from one hot tech of the day to the next hot tech of the days.

But let’s not stop on react.

> If you know JS/TS and are comfy in that

…there are better options than react.

> No one ever got fired for choosing react, as some other commenter said.

The the extent that’s true, I think it’s only in the sense that choosing react is more like a symptom of the shortcomings that get people fired.

Anyway, it’s the same urge to follow the heard that made react popular in the first place that will lead to its decline. New shiny things are calling. I hope we collectively choose better this time.

Well, I work for a Fortune 100 company and our standard is Vue, so, yes, using React could potentially get me fired ;-)
> No one ever got fired for choosing react

Well, the current top commenter says:

> I lost my last job because of it and all associated technical debt.

But I don't think a tool should be blamed there :)

But as someone that just learned (learns) react, the useThis and useThat stuff is confusing and lots of details to know how to write it so it doesn't do useless re-renders. It takes time to read and practice all of that.

(comment deleted)
> No one ever got fired for choosing react, as some other commenter said.

That's supposed to be a pejorative, to describe how what management wants is rarely correlated with what's good or useful. 'Ignore shiny stuff' is the rallying cry when the shiny stuff is less performant and harder to use; as TFA illustrates, the thing about React is everything is more performant and easier to use than it. Where is the value in using React instead of Svelte for new development? If it is just that you already know it, what do you think of TFA's assertions about learnability?

The difference between React and Rails is, well, pretty much every mentioned negative attribute of React - Rails neither is terrible for performance, nor is full of hidden pitfalls, nor requires lots of boilerplate for simple actions, nor is incompatible with most Ruby libraries, etc.

I really think 'new stuff bad' rhetoric here is being divorced from every real reason usually backing it. 'React is good enough' is like someone saying 'MongoDB is good enough' - no it isn't, it was a huge step in the wrong direction and everything invented since it is moving towards more streamlined versions of the old ways of doing things. You explicitly reference the resurgence of old-school stuff, without noticing that it the thing it is specifically a rejection of is React.

> The difference between React and Rails is, well, pretty much every mentioned negative attribute of React - Rails neither is terrible for performance, nor is full of hidden pitfalls, nor requires lots of boilerplate for simple actions, nor is incompatible with most Ruby libraries, etc.

You cannot be serious. Every single one of these points is a criticism I've seen leveled at Rails (in many cases on this very website) at some point or another. Performance? People wouldn't shut up about how slow it was compared to traditional server-side languages/runtimes which could do multithreading, or (later on) event-loop-driven runtimes like Node.JS. Hidden pitfalls? I'd like you to meet my friend, Mr. N-Plus-One Query. Boilerplate? I suppose this is a fair point, but on the other hand there were tons of people complaining that there wasn't enough boilerplate- that it was too magical. And incompatibility? Can I point out the libraries that weren't just written specifically to be used within Rails, but actually depended on its heavily-customized initialization and environment preparation?

> People wouldn't shut up about how slow it was compared to traditional server-side languages/runtimes which could do multithreading

? There has never not been multithreading. And yes, people said it was slow, and they were wrong at the time, having confused single-threaded performance with scalability.

> I'd like you to meet my friend, Mr. N-Plus-One Query

This is a general problem in all ORMs, it's got nothing to do with Rails and the closest anyone has ever come to solving it is LINQ, which still isn't very close.

> Can I point out the libraries that weren't just written specifically to be used within Rails, but actually depended on its heavily-customized initialization and environment preparation?

You can, but it doesn't really have anything to do with the point: there are Rails libraries that don't work outside Rails, but Rails is not incompatible with wide swathes of non-Rails libraries. The problem with React is not the quantity of React-only packages, it is that many non-explicitly-React-only packages don't work.

> I have a confession to make: I’m still not exactly sure what the difference between useMemo and useCallback is—or when you should and shouldn’t use them—even though I literally read multiple articles on that exact topic earlier today. (No joke.)

> I have a second confession: it’s still not intuitive to me what should and shouldn’t go into the useEffect dependency array, or why...

Come on, this isn't that complicated, I find it hard to take the criticisms seriously when the author hasn't groked React properly.

(comment deleted)
I'd be frustrated with React too if I worked in team where these things are not well understood.

You don't need to read "multiple articles" about these things, they're explained clearly in the docs

- https://react.dev/reference/react/useMemo#memoizing-a-functi...

- https://react.dev/reference/react/useCallback#how-is-usecall...

- https://react.dev/reference/react/useEffect#specifying-react...

To answer OP's questions, useCallback is

  function useCallback(fn, dependencies) {
    return useMemo(() => fn, dependencies);
  }
As for useEffect, everything that might change between renders, eg props used in the effect, must go in the dependency array.
The linter plugin for hook dependencies is pretty great, I very very rarely need to specify a dependency array different from the one the linter suggests.
I found this a frustrating and misleading post:

1. Starts talking about empiricism, does not actually deliver on it

2. Talks a lot about how React-things are old-actually, and then talks about signals and two-way data-binding as if there wasn't a time before React where these things were also pushed. React's non-adoption is not an accident I think, and these kind of posts would be more interesting if they would leave the surface-attacks and actually engage with the philosophy behind React's approach[1].

3. Quoting Alex Russel suggests you are more interested in heat than light (which is what he is known for where I'm from)

4. Distorted guitars are good actually.

[1]: https://gist.github.com/sebmarkbage/a5ef436427437a9840867210...

What do you mean by:

> Quoting Alex Russel suggests you are more interested in heat than light

I know he's said a lot of harsh things about frameworks like React, Css-in-js, etc, but I've read all of them from the perspective of "these things are hurting the UX a lot more than you think".

I had a PM suggesting we move from using git to dropbox because github is "hurting the UX".
Honestly, if that's what the research shows I think that needs to be considered. It'd be healthy to challenge those claims, and engage with how and why your revision control tool is hurting UX.
That gist looks like a bit of a list of strawman arguments to me. Most of those have never occurred to me, but lets look at one that has

"Stale closures in Hooks are confusing"

Seb gives an example under that section that they call confusing. I don't understand why. The syntax makes it very clear when the values are checked - if its in a closure, its clearly at the time of the click - if its not in the closure, its clearly at the time of rendering, so no, they are obviously not equivalent.

The problem with React hooks is that they behave very differently from all normal JS code. In normal code, most functions that contain closures in them run once and the closures run zero or more times (depending on the consumer of those closures). In React, the render function runs many times, but the inner hook closures run a different (could be smaller, equal or greater) number of times, where that number may depend on the number of times render runs or the number of times other closures run.

You can easily create a mess this way; React realized this, which is why the official documentation recommends not modifying state from within useEffect. But current frameworks (e.g. Remix) already do this, and this already causes problems with batching (see https://twitter.com/oleg008/status/1680290734644002816). Additionally, dependencies between multiple useState and useEffect calls can easily be non-obvious - they may be embedded within custom hooks.

In a mutable / signal based framework that really pays attention to all these details (like MobX) there is a consistent way to how things work based on a relatively simple dependency tracking mental model (MobX based signals can be implemented in about 50 lines of code if we're willing to forego the nice proxy syntax). So not only is state management way easier, its also on average less prone to these kind of issues.

In essence, React state managements makes things harder with the promise of making state easier and more consistent to manage long term, but doesn't really succeed in delivering on that promise. At least not when compared to more newer interations of mutable reactive state management (like MobX) which support all the goodies (automatic dependency tracking, automatic batching, automatic disposal of unobserved state and computed values etc)

> The problem with React is that behaves very differently from all of your code. With most functions that contain closures with them, the function when called runs once and the closures run zero or more times.

I don't think I agree with that generalization. I probably could not even make a generalization about what normal closur-ing does or should look like.

> In a mutable framework that really pays attention to all these details (like MobX) there is a consistent model to how things work. So not only is state management way easier, its also on average less prone to these kind of issues.

My experience with MobX and the greater observable-industrial-signal-complex is that accidental loopiness very much can and does still happen. I have seen codebases with complex MobX computed-changes-graphs that senior engineers were clever enough to write but not to debug. And I was not aware of tooling which helped keep these graphs in line.

That said, I agree React's hooks have friction to them. I just tend to be in the camp of "I prefer my computational change graphs to be gated by a bit more boilerplate".

> My experience with MobX and the greater observable-industrial-signal-complex is that accidental loopiness very much can and does still happen. I have seen codebases with complex MobX computed-changes-graphs that senior engineers were clever enough to write but not to debug. And I was not aware of tooling which helped keep these graphs in line.

My experience is completely the opposite. I've worked on large, complex interactive editors writtein in MobX and its been pleasently easy to reason about. Some tooling is built into the framework (cycle detection, strict mode). The design guides you towards making the right choices (prefer computed over autorun, etc) and makes those choices easy to use.

And unfortunately, with useState, useMemo and useEffect you can make the same cyclic dependency spaghetti (e.g. useEffect can easily setState). Unlike with MobX however, everything is harder.

Huh interesting, do you happen to have a link that goes into cycle detection? If it comes with MobX out-of-the-box, I can say that it did not seem to have kicked in for our use cases (but maybe it's an opt-in?)
I think its always been built in, but pertains to `computed` values only. Its still possible to create cycles but you need to combine `autorun`, timers/nextTick/similar and actions to do it - definitely not something thats needed in most code.

Some other methods for tracing and debugging complex graphs are described here https://mobx.js.org/analyzing-reactivity.html

Neat, thanks for the link!

I vaguely remember my colleague having a reason for why his abstraction did not fit in the computed box. Probably something related to async, so it might very well have been because of that

Forgot to add this:

> I probably could not even make a generalization about what normal closur-ing does or should look like.

I would say the key reason why this looks so wrong is that it essentially breaks lexical scope. The closures "look like" they are the same values from the scope above, but they are actually values from previous runs.

I would be hard pressed to find another library API that breaks lexical scope reasoning for its closures in such a way.

(comment deleted)
> Developer experience (DX) shouldn’t ever supersede user experience (UX).

That's a business decision, and not a given

Why would a business want to prioritize the experience of its developers over its users?
Because users do not know that there's some hypothetical "better" experience they could have had and do not care, unless your service/tool/whatever is not functioning correctly. Prioritizing your employees' enjoyment and experience to deliver more, faster, and consistently, is in all likelihood a better decision than prioritizing some subjective improvement to user experience.

Of course there are exceptions. But it's definitely a hot take to say you should never prioritize DX over UX.

> Because users do not know that there's some hypothetical "better" experience they could have had and do not care, unless your service/tool/whatever is not functioning correctly.

If your business competitors prioritise UX over DX, your users will soon know. And then they won't be your users any more.

Many users will prioritize the features they need, shipped promptly, and without bugs, over a nicer UI.
This is one of those "false delima" logical fallacies. The idea that you have to choose between a "nicer UI" and "many features, no bugs, shipped promptly".

All those are important, and given a talented team, there is absolutely no reason why you cannot have good UX, lots of features, few bugs shipped on time.

Yes, and going back to the original point, a company’s posture on this is entirely a business decision and not a “UX MUST BEAT DX” truth
Going back to the original point, "the features they need, shipped promptly, and without bugs" you speak of is actually part of the UX (user experience)!

Maybe you are mixing up the UI design with the UX? In any case, the original point is just highlighting the importance of UX, without giving any solid examples of DX.

Iteration speeds, I could see myself using a slower framework to ship faster.

See usage of React Native for example, it certainly produces apps worse than native ones, however many businesses still choose to work with it because you can improve the developer experience and ship faster.

Developers building stuff faster leads to a better user experience.
Developers building stuff faster leads to either building more stuff or working yourself out of a job. There's absolutely no guarantee that shipping faster leads to better UX.

I've worked in quite a few companies that were always pushing to ship faster and we're okay with the trade offs in missing tests and worse UX. The talk was always that we'd ship fast now so we can step back and clean up later but that's extremely rare in my experience.

For example if a better DX leads to faster development or less bugs. Especially when the difference between the delivered UX will likely be performance related rather than a functional/visual difference. Nearly all technology choices make trade-offs like this.
Glad to see these ideas being shared more broadly. I've had enough of React. I lost my last job because of it and all associated technical debt. The company I worked for (a startup) turned into a kind of Darwinian Squid Game where those employees who were able to keep adding on top of the mess of tech debt at a fast rate were promoted and those who did not were let go. It became an absolute brainf**. Also, I had lost all motivation because there was no real engineering left to do; all the work was related to debugging React-specific gotchas/issues which I hate doing.

React creates layer upon layer of unnecessary, opaque abstractions which make it harder to debug complex front ends and it encourages poor engineering practices. For example, I've seen a lot of cases of junior developers dump setInterval() calls all over the place into the code inside useEffect and not cleaning up afterwards... Causing memory leaks. But these devs can work so fast using React (to get features out in production quickly) that managers just love them and it's difficult to convey to them that this is in fact a short-term approach and it will come back to bite them in the long term. The app at that startup had such awful memory leaks that my new computer would freeze a few times a day.

I feel like the new generation of devs got used to working with these abstractions and by some measures they seem highly competent, yet it feels like they're missing some critical knowledge; like they don't understand what's happening under the hood. Sad because there's actually a lot less happening under the hood than there is happening above it! You can solve many problems while staying closer to native browser features without all this complexity.

+1

Have you been exploring any other technologies since React?

(comment deleted)
For my newest project, I'm going for plain HTMLElement (Web Components) but if I find that I can't live without reactive data binding/rendering, I will probably use Lit https://lit.dev/

So far it looks like I won't even need to go there... It turns out it's pretty easy to manage a web-component's dynamic state without reactivity (at least based on how I architect my apps). I find the HTMLElement lifecycle hooks (and attributeChangedCallback) to be sufficient.

A lot of the React fanatics are in denial about this. But, I've seen some absolute messes. Does anyone remember InVision? You probably have forgotten about them because Figma and other design tools ate their lunch, but part of their failure and delay in InVision Studio being released was in part because of React. I know other companies have encountered limitations with React and had to hack around them, most notably Atlassian who had to break their app up into apps (essentially parts of the app were smaller apps) because they wanted to use state management for everything and the memory problems were astronomical.

React got popular because at the time, Angular.js was the framework of choice and we can all agree Angular had some serious problems like the digest cycle and performance issues working with large collections. React in comparison to Angular was a breath of fresh air, but in 2023 there are far better choices now than React.

The thing is, React started out as just a view library and the community are mostly to blame for how terrible it has become. How many state management libraries have there been? How many router packages? Devs think they want a library, they actually want a framework. It's why the most popular uses of React are not vanilla React, they're frameworks like Next.js. Also, when you compare the performance of React to other libraries and frameworks, you realise it's one of the worse performing options. It's slow. The whole entire notion of Virtual DOM was groundbreaking in 2013, but in 2023, reactive binding approaches like those taken in Aurelia and Svelte are far superior and better performing. Virtual DOM is overhead.

Has anyone ever seen a large-scale React project that is just using vanilla React? I've seen a few large-scale React codebases and many of them were a tangled spaghetti mess. It gets to a point where React's lack of opinions and standards for even the most basic of things mean you can see the same thing implemented 100 times in the React community. If you're building something beyond a landing page or basic CRUD app, you need some conventions. There is a reason despite the hate it gets, Angular is still used in enterprise and government settings. It's verbose, but you write components and other facets of your apps a specific way and know that Developer A implementing a feature is going to be understood by Developer B because it's not going to be so self-opinionated. This is something that backend frameworks learned years ago.

Don't get me started on the terrible communication from the React team. A good example is how they have handled React Server Components. They changed the docs to recommend RSC's by default, despite the fact many community packages don't work with them or require additional packages. The way they approached RSC's and rushed them out was terrible. This isn't the first time either.

I have been working with Aurelia 2 (https://docs.aurelia.io) and I love it. It's intuitive, it's fast, the developer experience is great, it comes with all of the needed dependendencies you need like a router, validation, localisation and whatnot. No need to go building a faux-framework of Node packages bloating your app. I've also been working with Web Components, which are in a really good place now too and getting better with each proposal (there are a few good WC proposals in the works right now).

Developers need to start using the platform more. Web Components have been supported by all browsers since 2020 (Chrome has supported them since 2013), so it's a good time to dive in. Using something like Lit gives you a nice development experience. Part of the problem is React developers have been brainwashed into thinking classes are bad because of some terrible design decisions in React (which led to Hooks) and writing Web Components relies on using class syntax (although, you can write them as fu...

I have worked with Aurelia 1, and I strongly recommend against using this framework. Over time, I have collected many gripes, but just off the top of my head:

- Arrays are observed by monkey-patching the push, pop, and other methods. There is no concept of assigning keys to cells [1]. So, if you want lists to be stable, you must manually diff the arrays and mutate the old one.

- In general, the observation system was awful. They have a custom JS subset interpreter for their templates, and they secretly add properties to observed objects. If all else fails, they will start up a 300ms timer that polls for changes.

- The binding system favors mutations over functional updates, but deep-observing objects isn't possible. So, if you want to observe a data structure for changes, you may need to write down each key.

- I encountered multiple bugs in the templating language. I don't remember the exact details, but they were similar to this: If you have a containerless element somewhere inside a conditional element, that's somewhere inside a repeated element, the containerless element isn't rendered.

- No type safety in their templates.

- No conditional slots, no dynamic slots, it's not possible to forward slots, can't detect if a slot is used.

- In my tests, performance was worse than React.

[1]: Unless you dig through GitHub issues and find an external contribution. However, it was broken for some time and doesn't follow the Aurelia conventions.

These are all Aurelia 1 concerns which have been fixed in Aurelia 2.

- There is no dirty-checking in Aurelia 2. The observation system now uses proxies and other sensible fall-back strategies. The computed decorator for getters is also gone in v2, meaning no accidental vectors for dirty-checking.

- Observation system was rebuilt to use many of the same strategies detailed in point one. No dirty checking and proxy-first. Similarly, your next point about mutations, also has been addressed by the new binding system.

- Many of the templating bugs people encountered were spec implementation issues due to how the browser interprets template tags and content inside them. There were a few repeater bugs, but the ones outside of non-spec compliance haven't been a problem in years and do not exist in Aurelia 2.

- You can write type-safe templates now.

- You have have conditional slots now if you use the new au-slot element. A lot of the slot limitations in Aurelia 1 were because Aurelia adhered to the Web Components spec for how slots worked. In v2 there is still slot, but a new au-slot has been introduced to allow you to do dynamic slots, spot replacement, detect if slots are defined or contain content.

It's important to realise Aurelia 1 was released in 2015, so it's not perfect and some design decisions reflected the state of the web and browser limitations at the time. Aurelia beat out React in a lot of benchmarks back in the day. I'm sure Aurelia 1 vs React has slipped, but Aurelia was one of the faster options for a while, especially in re-rendering performance. You should give v2 a look. It improves upon v1 in every single way.

how is the transition from aurelia 1 to 2?

my app has actually very little aurelia specific code so i expect this should not be to hard. if only i can find an aurelia 2 equivalent of this version that works without any build system: https://news.ycombinator.com/item?id=36971080

The syntax and overall paradigm of Aurelia 2 is the same. The team avoided where possible a repeat of what Angular did to the community with the transition to Angular 2. Most notable differences are routing and dynamic composition.

There is a build system free version documented here. Is this what you mean? https://docs.aurelia.io/developer-guides/cheat-sheet#script-...

If you need any help porting it over, just let me know.

that looks interesting, but it doesn't seem to work.

https://unpkg.com/aurelia/dist/native-modules/index.js doesn't resolve. i tried https://unpkg.com/aurelia/dist/native-modules/index.mjs which does resolve, but it links to a dozen other files which all don't seem to resolve either. it looks like unpkg.com is rather broken.

with aurelia 1 there was a downloadable archive (which still exists) that had everything bundled that i could just unpack and it was ready to run. it didn't even need a server to host the files if i was running a browser on the same machine.

the original documentation for that is here: http://web.archive.org/web/20160903072827/http://aurelia.io/...

basically there are two things i am looking for:

i want to be able to develop the application without using browser-sync or build steps and it appears the version you linked promises that.

but i also want to be able to host and develop the application completely offline without any need for internet access.

the reason for that is that i am using the application (in production as it were) while i am developing new features or fix bugs that i discover while using it.

running the transpiler in the browser doesn't bother me, it's been fast enough so far.

i would use aurelia-cli if i could figure out how to make it build a development version without browser-sync and without transpiling and compressing the code before it is deployed.

Aurelia 2 has been in alpha for years and is now only in beta.

My point wasn't only that these issues exist. If a framework has this many issues that go unfixed for years, while the user-base dwindles, maybe you shouldn't trust the developers.

In addition, I believe that not only the implementation, but the fundamental design that is flawed. There's a reason why UI development has moved away from the OOP/Mutation/MVVM approach. The problems that hooks were intended to solve are real, and every big Framework since React has provided approaches to solve them... except Aurelia.

is there any framework that works similar to aurelia but does this better?

i really like how aurelia is practically free of boilerplate code, and doesn't force my code and data into specific structures.

I've been working with Web Components a bit lately and was pleasantly surprised to see Lit had some similarities to Aurelia. Nothing really comes close to Aurelia, which is surprising given it has one of the better developer experiences.
> The fact that React is ten years old and doesn't support Web Components still, goes to show just how little the React team cares about Web Standards .

No one cares about web components, including people who originally where really bullish on them (Vue, Svelte, Solid). React supports web components just as it supports anything that attaches itself to the DOM, and that is more than enough for the vast majority of use cases.

Meanwhile, if Web Components were any good they wouldn't need another 20 specs just to barely patch holes in their design: https://w3c.github.io/webcomponents-cg/2022.html or still have unresolved issues like "custom button cannot submit a form": https://github.com/WICG/webcomponents/issues/814

> but at least jQuery helped shape modern standards. Has anything good come from React for the platform?

It could have, if the people behind web components listened to anyone except themselves.

I'm certain that this would have happened with any other framework mentioned as well and pointing towards React being the issue is just searching for a scapegoat where there is none
The problem starts in html, css, javascript and the browser as a development platform. React tries to patch over those issues, and creates some new issues.

People claim other frameworks solve the same issues without introducing any significant new problems. I'll remain skeptic until I see a couple of huge full-functionality apps in those frameworks that manage to keep a clean codebase and good performance.

I've seen too many times people "solving" frontend development, most of the time that solution just amounts to documents linked to each other, and avoiding complex forms and interactivity, but if you just do linked documents, frontend development was solved in the 90s, and you don't need javascript at all.

You can say this about any technology though. Whenever you prioritise delivering features and are ambivalent about tech debt then of course the quality of the code base will suffer.

There is nothing inherent to React that makes it significantly worse than the many other Javascript frameworks out there. At least it has the benefit of having ongoing support by a major vendor with a record of long term support for open source projects. That's not something that is all that common in the Javascript ecosystem.

> There is nothing inherent to React that makes it significantly worse than the many other Javascript frameworks out there.

There definitely are plenty of things which are an order of magnitude harder in react than any other framework.

One flow data is an example, having to install deps for everything from state management to css is another.

All the choices compounds and make complicated projects even more complicated, increasing tech debt.

Don't get me wrong: Angular (and for sure other frameworks I can't think of) had the same problem; many other frameworks do not.

These days I'm very happily developing against Solid Start with not a single issue I can't think of and a syntax which is very close to React.

"There is nothing inherent to React that makes it significantly worse than the many other Javascript frameworks out there."

That is exactly the kind of conclusions that come from not understanding what runs under the hood.

I agree with most of it, but I still had to chuckle at the "don't understand what's happening under the hood" part. The browser and JavaScript are themselves very high level, so it's funny how e.g. not using React would make you understand more what's going on under the hood. It's a pretty deep hood.
(comment deleted)
i think the popularity of react (and node) comes down greed. do more with less. a tumorous outgrowth of the "move fast and break things" attitude of early FB development and cargo culture of the tech boom eras.

you can have one dev write the front end and most of the server side code too. not only that but they have to handle much of the tooling, testing and debugging.

and the learn2code push also was designed to saturate the market with lots of these devs. many of whom lack the background knowledge to understand what they're doing.

i seen this job expand through the years from a smaller set of responsibilities to a much larger one where most of your work is only indirectly (and irritatingly) related to what you're actually trying to do.

(comment deleted)
> I feel like the new generation of devs got used to working with these abstractions and by some measures they seem highly competent, yet it feels like they're missing some critical knowledge; like they don't understand what's happening under the hood.

Is this remotely relevant, though? I don't think it is. The whole point of working with higher level abstractions is to not have to care about the lower level bits. We all know that things are not as optimized as they could theoretically be, and that's ok. People are paid to deliver value, and microoptimizing stuff just wastes everyone's time.

It's fine to work with higher layers of abstraction, but you should get some value out of it. What I think happened is that React just removed some work from lower layers and created even more work at a higher abstraction layer.

I would say that React gives you more work in terms of cognitive load and also in terms of development time if you look at it on a medium to long time-frame.

I think large systems get so large and complicated that they become very hard to reason about simply and think about. They become difficult to change their architecture or tweak them. So developer velocity takes a hit and the system is so complicated that it's hard to change anything.

LibreOffice, Mozilla Thunderbird are of such scale that it's difficult to change these applications how they work today.

If someone is trying to get a job, would React be a good option? I'm planning to shift to some role where I program, right now I'm in security, and web browser and JavaScript stuff seems the most interesting to me, I have just started recently on this journey.

I am creating a project using React + Vite which could have very well been created in vanilla JS, but just to get familiar with React and also to show some proof to prospective employers. So just wanted some advice on whether React is the way to go, or is Vue/Svelte where the industry is heading towards? Also any other tips on what you would be looking for when hiring a dev would be very helpful. Thanks.

I'd say it's a good thing to know the approaches of different frameworks, but solid foundations are the most important factor in successful development. Try to decouple your application logic from your view layer to clearly delineate between the React/UI parts and the rest. This would also make it easy to later build a Vanilla version of the frontend without rebuilding the whole code.
If you're just starting with programming in general, then I would highly recommend exploring React, as it is a very intuitive kind of way to learn Functional Programming as a paradigm. Sprinkle those map, reduce and filter functions around, avoid Redux for as long as possible, and you get a pretty good idea of what immutable state does to improve code quality. Haskell and Lisp won't seem so exotic after that, so you'll be able to appreciate the more subtle advantages of FP later.
Usually mediocrity becomes popular. If you want the best you have to look into niches usually crowded with great above average people. Be it music or software.
> Vue uses a templating language closer to default HTML than to JSX, which makes it much easier to write conditionals and loops in template files, without having to reach for workarounds like map and ternaries

I like react/jsx precisely because I can use language constructs like filter, map, reduce, etc, instead of a custom implementation of basic things like if statements and loops. I find this much more ergonomic and didn’t realise some people see this as a “workaround”.

Worth also pointing out that when you write these templates, as strings or template literals, they might resemble HTML in appearance but really have no relation to actual HTML except what they output after processing. All of the added directives also have no equivalence in HTML. You’re just writing JS indirectly.

But you want to avoid using to much code in HTML, because this isn't "page.php?page_id=welcome" anymore..
Plus, the power of being able to treat JSX as any other JS object means that you can use standard JS constructs like loops - slightly contrived example:

  const items = [];
  let runningTotal = 0;
  for (const item of someArray) {
    runningTotal += item;
    items.push(<li>Item {item}; running total {runningTotal}</li>);
  }
  return (<ul>{items}</ul>);
The closest example I can find in Vue would require a computed property to implement something like this.
Disagree. I would argue your example is hard to read compared to the equivalent in Vue, and it mixes business logic (computing the running total) with the rendering logic.

  <script setup>
    const items = [1,2,3,4,5,6]
    let runningTotal = 0

    const getRunningTotal = (item) => {
      runningTotal += item
      return runningTotal
    }
  </script>

  <template>
    <ul>
      <li v-for="item in items">
        Item {{item}}; running total {{ getRunningTotal(item) }}
      </li>
    </ul>
  </template>
No need for a computed property. Plus, getRunningTotal can be unit tested, or extracted to another file for reusability.
Extracting function to separate fine in react would be equally easy . You just extract and import it.

It is just that above example is so short and readable, that you don't have to bother.

Does syntax highlighting or lsp stuff work for things like v-for?
You've still mixed the logic, just in a horrendously oblique manner. If you rendered the list twice the second list's running totals would be wrong from re-use of the global variable (in what is meant to be render-only logic).

If you're after keeping the concerns separate you would need to precompute the running total for each item.

See also: mustache(5) which completely embodies this philosophy.

It's technically possible, but it looks horrible and should be avoided because of lack of readability. Keep the templates plain and simple, and store your business logic elsewhere.
I don't think this is a good way to build JSX from lists. Better to keep components smaller. Also this way will show React linting rules, like the "key" requirement.

  return (
    <ul>
      {someArray.map((item, idx) => (
         <li key={item.id}>Item {item}; running total {idx + 1}</li>
      )}
    </ul>
  );
This. Most of the value-proposition of JSX is that it is just JavaScript and as such you just write code logic like you would normally do. Need to filter some items? You can do that with the Array.prototype.filter method you already know. Do you need to return a list of components based on some list of object values? Just use Array.prototype.map.

I never really understood those that see this as a weakness and would rather learn a more limited set of template string functions that work kind of like, but not entirely like, the native JavaScript collection functions.

Yes, using a ternary operator in JSX to conditionally render components/values looks a bit ugly, but nothing is stopping you from making a wrapper component like:

<If cond={...}><div>Conditional content</div></If>.

Also with JSX/TSX scopes behave exactly like they do in regular JavaScript, because it is regular JavaScript. Templating languages often invent their own form of scoping mechanism.

> nothing is stopping you from making a wrapper component like...

I'd suggest avoiding that, since the inner JX expression will be evaluated even if cond is false. In the best case this just means some unnecessary allocations, but in other cases it can cause unexpected errors (e.g. you check obj != null then try to use it inside the if).

Yeah you have to do

  <If cond={...} then={() => 
    ...
  } els={() => 
    ...
  } />
so the `then` and `els` branches only get evaluated if needed
Which has ugly untax IMO
We use ternary extensively with in JSX. Keeps the logic in vanilla JS

{ some_condition ? (

  <H2>It was true</H2>
) : (

  <H2>It was false</H2>

)
Even though I'm a JS programmer myself and I do use similar constructs once in a while, I have to admit that this gets dangerously close to the Greenspun's tenth rule... In times like this I miss proper Lisp macros.
Good point. You'd have to pass the stuff to be rendered as a function that would be run if the conditional is true. In any case you just add another allocation, scope (the anonymous function), etc. But some people might like the "syntax" a bit better.
> ... the inner JX expression will be evaluated even if cond is false.

Well, actually. it don't have to be. Just React decides to compile JSX in that way.

Vue 3 also supports JSX. But it keep the content of children lazily evaluated.

Their compiler compile a JSX `<Parent><Child /></Parent>` to structure like `h(Parent, {}, { default: () => h(Child, {}, {}) })` . So the child jsx tag is never evaluated unless the Parent component want to.

It's really not a syntax limitation because JSX never specify that the children of element must be eagerly evaluated.

> It's really not a syntax limitation because JSX never specify that the children of element must be eagerly evaluated.

That seems like a dangerous reinterpretation of JSX. It's just a literal syntax, it's not meant to be lazily evaluated.

Having a JSX compiler interpret <Parent><Child /></Parent> as h(Parent, {}, { default: () => h(Child, {}, {}) }) would be like having a JS compiler interpret

   let parent = {
     child: {}
   };
as

   let parent = {};
   Object.defineProperty(parent, 'child', { get: () => ({}) })
That's just... not what that syntax means. It should be

   h(Parent, {}, [ h(Child, {}, []) ])
Saying 'JSX doesn't specify it' feels like playing a 'there's no rule that says a dog can't play basketball' card.
It's a framework specific DSL anyway. (Unlike e4x, which is actually a standardized feature). There isn't a spec decides how it must be interpreted. And it also don't matter as long as the specific framework that use jsx output standard javascript that browser can understand.

In practice, every framework interpret jsx in slightly different way. React has own. Vue has own. Solid has own. And even react itself interpret JSX in 2 way (the new and old jsx compiler). So I feel there really isn't a rule that you can't compile jsx in some specific way because the output is never part of the specification of jsx from the beginning.

Interesting, I haven't used Vue so I wasn't aware that some frameworks do it differently.
> Yes, using a ternary operator in JSX to conditionally render components/values looks a bit ugly, but nothing is stopping you from making a wrapper component like:

Couldn't you put that in a function that returns the correct JSX element or am I misunderstanding the problem? Something like:

  renderThing(number) {
    if(number > 0) return <div>...</div>
    return <div>...</div>
  }


  const Page = () => {
    return <div>{renderThing(someNumber)}</div>
  }

Simple ternaries are fine IMO but thats what I do when the logic gets complicated. I never used a nested ternary in that situation, for example.
You have to be careful with code like the above in React. Using a lower-cased function that returns JSX but is not rendered with react.createElement (either directly or via the JSX syntax) can lead to confusing violations of the rules of hooks as it relates to exact ordering of calls.

This is because it doesn't get registered as it's own component, so conditionally called children with hooks may cause errors.

The render function is a pure function returning jsx based on the input, not invoking any stateful operation like "functional hook".

So this style of code is fine in react

Does simply uppercasing it avoid these concerns?
I get using plain JS, but JS's lack of expression-statements (see rust for contrast) just makes most of it EXTREMELY awkward.

Ternaries, booleans that cast to JSX, switch statements wrapped in IIFs...

Once you try any languages where (almost) anything is an expression, it is hard to accept statement-oriented languages
That's true. But in JSX you have the extra constraint that you can't really use imperative code so this magnifies even more.
Indeed, having if-then-else being an expression (as well as switch) would be great in JSX. It seems there's a proposal for a match expression in TC39...
The crazy part is that there's not a really good reason why we couldn't make if..else function as either a statement or expression depending on the context. I think there's a proposal to do that.
Indeed, at least there's a TC39 proposal for that. It should be the default for all statements and it's shocking that python went to define the new match clause to be just a statement and not an expression
Especially with copilot, which is clearly well trained on React and JS, I find I often just need to write the first few characters of an array method or other common method and it will generate 10 or so LOC that are at least 90% of what I want.
I liked JSX a lot when I was using React. However, writing business or view logic in templates is a maintainability nightmare. With JSX it's very tempting to do this and I did this often, but it's still a mistake and I regretted it every time I visited my spaghetti JSX months later.

When it comes to comparing React with Vue or Svelte, the templating is not the critical factor.

The JSX bits in a React codebase aren't templates, at least in the traditional sense where a template is a static file that looks like some desired runtime output and contains some "slots" which are analyzed at build time (or program start time) and then filled in with dynamic values at runtime. JSX kinda looks like that, but there is actually no "templatey" stuff happening at build time, i.e. React does absolutely no analysis whatsoever of your JSX and has no idea what it contains until each new time a component renders at runtime.
Stricly speaking it's a render function indeed, but it does serve the same functional purpose as what people usually call "templates", i.e. defining a dynamic view only once.
That’s true, but the technical differences between a render function and a traditional template is arguably the biggest fundamental difference between React and most other UI rendering tools.
I totally agree. Templating is not the critical factor indeed. The templates shouldn't contain business logic so they should be simple by default. If not then you're following an anti-pattern.
In defense of those directives and template languages, they compile down to efficient operations that every developer can take advantage from. I like that `v-for` is likely more efficient in regards to how it operates than how an average developer might write such a function, especially when dealing with nested data or more complex situations. Other efficiencies can be gained by using a template language as well via the compiler.

Not that you can't with JSX (Solid and Qwik use JSX and have transparent ways of doing optimizations as consumer of those frameworks) but its not currently the default, most (if not all) optimizations must be done "by hand", is a big tradeoff for some places.

This can be the case, but as I understand it typically isn't so, at least with most big frameworks. For example, I believe Vue templates are typically translated to a hyperscript-like render function that has a similar efficiency to the average React render function. Indeed if anything, I would imagine Vue would be slightly less performant, because the v-for loop is quite abstract and can handle things like looping through objects, so there must be a reasonable amount of machinery to handle all the different loop cases.

The state of the art is less about handling things like loops, and more about reducing the amount of work needed to render static elements. For example, say you have a single component that renders a static table, and in each cell of the table you need to render something dynamic. In the classic vdom system, you'd construct a `table` node, with a `tbody` child, which in turn has `tr` children, which was contain `td` nodes, and then finally in the `td` nodes you render the dynamic content. This works, but most of the content isn't actually changing, only the contents of the `td` nodes. What modern frameworks like Solid and Svelte do is to compile that whole nest of nodes into a single HTML string, and then add instructions that specify where exactly the dynamic content lives. That way, on the initial render, the browser is doing more work natively, and on later updates, only the new content needs to be checked and changed. I know there's a library for React that adds similar capabilities there, albeit with a few more constraints than in other frameworks.

I do know Solid does for-loops differently than in other frameworks, but this is less to do with efficiencies gained by compiling, and more about how to optimally use the signals system that it uses for reactivity. Apart from that, I don't really know of any optimisations that would make something like `v-for` any faster than `.map(...)`.

the Vue compiler does do optimizations you can't get if you were to write the components by hand, most of the time.

I did only use `v-for` as an example of sorts though, and in the common case, `v-for` vs a regular `for` loop are similar if not the same, IIRC, but in some cases where your children may show or hide based on downstream data, it can be more efficient

This discussion never ends. It's so subjective and totally not important at all. Templates shouldn't contain complex logic anyway, only formatting logic with some for loops. You can learn any syntax in 30 minutes if needed. The battle isn't fought on the hills of templating logic. It's sad because there are many interesting differences other than templating logic between frameworks, but they are less superficial so they get little attention.
You know you can use JSX with Vue right?

https://vuejs.org/guide/extras/render-function.html

GP was reacting to this statement from the article:

> Vue uses a templating language closer to default HTML than to JSX, which makes it much easier to write conditionals and loops in template files

In practice with Vue you tend to write that stuff in JavaScript (eg in a computed property) and keep the HTML as simple as possible.

I've done a lot of Vue and your bit of React and overall I prefer the Vue way here.

The article starts by a quote of Alex Russel, paid by Google to specify Web Components between 2008 and 2021 - a technology in direct competition with React at the time. He is not a technologist to track or quote to get unbiased framework opinions.

I don't agree with most of the argument here, to the point where I think we live in totally foreign echo chambers that rarely exchange ideas. I didn't get any new idea out of this article, it didn't inspire me to do something I already tried 10 times in the past.

The website loads fast, and that's great. 10% of the CSS is unused. and that's a small blog. In the non-themed CSS, it's close to 50% unused. You could consider it a non problem - at scale dead CSS is complicated to manage, at least for me.

I don't feel it's fair to brand "web components" as a technology that was in direct competition with React. I think the nuance matters.

They may have been solving similar problems then, and they may be optimized for different things now. But Web components are a web standard, and React is not. Pushing for web standard technologies is inherently unbiased because that's just betting on the platform. This results in pushing for platform improvements (like declarative shadow dom so it plays well with SSR, etc), and then everyone benefits.

IMO it'd only be a bias take if web components were branded as having no issues whatsoever, or that they are the most optimal solution for everything. I think it's okay to say, "web components currently don't solve my problems and that's why I'm looking to use something else like React or Solid". However, I've not seen this sentiment shared when it's the other way around with React. It's always "no one got fired for picking React" instead of actually evaluating what will deliver the most value to your users.

> Pushing for web standard technologies is inherently unbiased because that's just betting on the platform.

There's a ton of politics around web standards, and you can bet if Google manages to push through Web Environment Integrity, I will not consider people suggesting to use it "unbiased because it's a standard"

That's a fair point. I didn't consider that
I'd take that point further and state that React is in no competition with anything.

React was invented because Facebook had very interactive/stateful UI as well as large teams of developers working on the same codebase. The then conventional jQuery soup approach didn't cut it.

Should the web platform come up with a standards-based approach to solve the same problem, then this is not "competition", it is a blessing. It's not like React earns Facebook any revenue. Ideally, it wouldn't be needed or exist at all.

React has more implementation of its components model (preact, react, solidjs, inferno,…) than web components (chrome, ff partially and safari partially), is more used and is more indépendant from the current web monopoly. Anything w3c is now a standard to keep dominance.
> I don't feel it's fair to brand "web components" as a technology that was in direct competition with React. I think the nuance matters.

Nuance doesn't matter to most proponents of web components. When they were introduced, for the first few years they were explicitly marketed as platform-native solution that makes frameworks obsolete.

Now the rhetoric seems more nuanced, but it's the same. It's only been upgraded to "build with lit".

> IMO it'd only be a bias take if web components were branded as having no issues whatsoever, or that they are the most optimal solution for everything

Mostly they are billed like that. And any voices criticizing them are either ignored or blocked.

> paid by Google to specify Web Components between 2008 and 2021 - a technology in direct competition with React at the time

I’m personally of the belief that we should assess arguments, not employer. Someone working on a competitor to React can make very valid arguments against it, they’re often some of the most knowledgeable people about the drawbacks of the framework.

I’m personally not a fan of Alex’s abrasive style but I’ve seen plenty of his critiques of sites written in React-based stacks and they’re usually evidenced with bundle analysis, burn down charts and so on. They’re not unfounded.

(I also disagree that Web Components are a competitor to React. Frameworks like Preact and Svelte are interoperable with Web Components and build on top of the API. React chooses not to but that’s neither here nor there)

Neither Preact or Svelte produce web components by default or use features like Shadow DOM.
I’m not sure how that’s relevant to the point that Web Components is an API that can coexist with frontend frameworks.
> (I also disagree that Web Components are a competitor to React. Frameworks like Preact and Svelte are interoperable with Web Components and build on top of the API. React chooses not to but that’s neither here nor there)

Used to work on Web Components (the browser side.)

Although we didn't take it as a competitor, in hindsight it was - at least in a sense. Both are component-based technologies, and the expressiveness of React and ES6+ made WC less relevant. I think WC is still nice to have, but it is no longer a must-have as it was originally envisioned.

The JS ecosystem has largely solved the problem by themselves without browser vendor's help (besides improvements of JS itself.)

At some point I'm really going to try to build my next SaaS app with Svelte. I'll write an equally dismissive post as soon as I run into the first roadblock, which I guarantee won't take long. I need big, bad, and fully customizable frontend/backend frameworks for building some giant static websites!

Let's take a look at the frameworks available for this...

Gatsby > 50K stars Next.js > 110K stars (!) Remix > 24K stars (impressive, only 2 years old!)

and looks like Svelte has... ultimately... Sveltekit. Svelte has Sveltkit:

Sveltekit > 15K stars

React is great; not because of React itself but the massive ecosystem and number of frameworks behind it. There isn't a component or hook under the sun you can't find.

I grow weary of such posts, still waiting for an objective truth as to what really differentiates a frontend framework and makes it so much better than all others; still haven't seen it.

One thing I'm coming to realize after decades or being an engineer, architect, and now manager/team lead who is still creating code.

In the thick of substantial engineering projects, when all your initial plans and architecture have not survived contact with the enemy [1], you realize that any robust, maintainable, really successful piece of software only gets that way by going against the zeitgeist of frameworks, patterns, tools, and architectures. Not only must you use boring tech, you must do some thing directly contrary to "best practice".

There are many examples to support my point: CloudFlare's "weird" internal architecture which is heavily DB-based (making heavy use of stored programs and the like) [0], to Google's use of all kinds of internal innovations: Protobuf, MapReduce, Closure compiler, etc., and more I can't think of now. It's a testament to the somewhat sad state of software as an engineering discipline that so much of our real world engineering has to go against state of the art.

For example, just the other day I came to the conclusion that all JS bundlers are "bad", unable to meet my need to reliability, efficiency, and speed. And my needs are not even at the edge. So, I am not exploring crafting complex front-ends without bundlers at all. The shiny things that are constantly being pumped are not subject to, or the result of, a long period of real world use. The examples used to illustrate most are toy examples with virtually no relationship to what the experience of using them in a substantial project would be.

And, yes, I kind of hate how React has taken over everything, although I like JSX, the idea of UI as a function of state, etc. So I had to write my own UI library to get away from React the library, while still being able to write UIs in a react-like way. I hate other heavy UI frameworks even more.

The way forward is to get back to basics. Learn from various libraries and frameworks, but do not import a million dependencies into your project, and do not depend on too many tools. I believe each piece of software must be crafted almost from scratch [2], embedding modern but tested architectural approaches, in an almost monolithic way.

[0] https://news.ycombinator.com/item?id=22883548

[1] The enemy here include complexity, performance issues, shifting requirements, time and cost estimation, and similar things that bedevil substantial software projects.

[2] Not totally from scratch, otherwise you would have to boostrap an OS, compiler, and stuff. But the black boxes you rely should be _very_ robust, and easy to reason about. No one gets confused about, or has to debug, what is happening when they save a file. You trust what the OS is doing , although it can be quite complex behind the scenes.

It's worth thinking about where "best practice" and "state of the art" come from. At worst, it's a business that's trying to shape these things to sell their product.

In a more middle case, it's just people explaining what they did when they faced a particular problem, and how well their solutions worked. There's a cynical version of this too, where people are more focused on their employer-brand or personal-brand than being helpful.

Not to downplay the actually helpful instances of thought leadership! I'm just observing that there isn't always an easy way to tell these things apart, or to boost the helpful ones.

I thought this was another "The things you forgot because of using the frameworks all the time" title.

It wasn't.

In general I really like React but after spending years tinkering with JS and seeing my codebases rot away faster than I could keep up due to ecosystem/tooling churn (I started in 2014 when React was ES5 and still using mixins), I started replacing my frontends with regular server-side rendered ones. I enrich these with vanilla JS and some smart things like using async fetch and the history API to seamlessly replace content when clicking links. There are of course solutions like HTMX or Turbolinks that do similar things but it's really easy to make something simple work with just JS.

With some of these tricks the resulting application feels very similar to a real SPA. Of course if you're really building an application in the sense that a lot of the computation and state handling happens in the browser you will still need to use JS, but even then I would just go with vanilla JS in most cases. That said my experience is from building mostly CRUD-like apps alone or in small teams, so I can see the benefit of more complex toolchains in larger organizations.

But yeah, browsers and JS offer so much out of the box, most people don't seem to realize how easy it is to do cool things without relying on frameworks.

Ok so this is topical for me right now. I somehow managed to avoid the world of react since it became a thing. All the code i was writing was pre react and we kept the old frameworks, and that’s was all good and well.

Fast forward to today, i took over a project that is full react. What a mental shift. Having said that, sometimes i love it, sometimes i hate it. A friend put it best when he said “i struggle to predict what might be trivial and what will take a full day or two”, and that’s my experience too.

I found myself just the other day wishing i could await the setting of a state variable, it was making saving a file in a complicated form a little difficult. I found a work around but i don’t love it and i know the next time i come back to work on it I’ll be scratching my head at what i was trying to achieve and why i did it this way. Naturally i over commented my rational here.

I do miss writing in basic js and server side code, it’s probably slower over all, but i do miss it for the simple things being predictably simply and the hard things typically being predictably hard.

You don't need any of it. You can write vanilla JS, using ES6 classes, as web components and be done with the whole front-end framework game. All you need for data-binding is an Observable of some kind that can loop through listeners for events and call their handler. Component classes all have a render function so that React kids feel at home. Those components are registered using CustomElementRegistry.define [1]. Tools like vite help roll up all that code and styles into a minified bundle but doesn't include all the crap that these "modern" frameworks add. Take this from someone who has been writing web sites since 1996. You don't need any of those frameworks unless you're looking to give away control of your product to the roadmap of the framework or need to hire a bunch of people to accomplish something quickly and all they know is React.

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

I'm assuming that someone who "took over a React project" isn't trying to remove all the react from the project. They did mention they found both up and downsides compared to their old work.
someone should write up some documentation for the standard APIs and how to use them but then give the website a name. People would think of it as the next amazing and shiny JS framework but, in reality, it's no framework at all just a name. That would be good medicine for the front-end dev community.
(comment deleted)
er, so is the advice here to write your own framework instead of using a framework? because writing and maintaining your own framework is not a trivial exercise and is a massive project on its own, which is why a lot of people reach for a framework in the first place...
> You don't need any of those frameworks unless you're looking to give away control of your product to the roadmap of the framework

Have... Have you seen the roadmap for web components? 20 more specs just to barely patch holes in the original design: https://w3c.github.io/webcomponents-cg/2022.html

Not sure if this is what you settled on, but I'd just initialize the state variable as null and then put it into the dependencies of a useEffect that fires when the variable is set
It’s a mix of that. This introduced a race condition and a frustrating rendering experience, but yeah it’s a flavour of this.
> I found myself just the other day wishing i could await the setting of a state variable, it was making saving a file in a complicated form a little difficult.

It used to be that React.setState would accept a callback function to fire after the state was set.

The newer setter function returned by useState doesn't have that anymore I'm pretty sure. Tbh I've never tried though.

if you need to await a setState call, you're prob structuring your logic in a less-than-efficient way. in whatever handler is calling setState, you should compute the next state in the handler, setState(nextState), then use that nextState for whatever comes next.
I don’t know, i agree it wasn’t in the react style but it was in the style of basic logic

On change detect new file is there, Set file to variable, Call api to save state.

The issue is setting the variable and calling save is now a race condition.

I realize this is unsolicited advice, however this sounds like you're hitting the "DOM has an imperative API but React is declarative" barrier, because React (until `use` ships at least) has no way to just await reactive values, you can:

if sufficiently complex, you may want to look into `useSyncExternalStore`[0] which can be used to store and update state (IE maintaining UI reactivity) in a microcosm.

Otherwise, I'd recommend framing it as a series of dispatch-able steps, which maps well to `useReducer`[1] possibly in combination with `useEffect`

[0]: https://react.dev/reference/react/useSyncExternalStore

[1]: https://react.dev/reference/react/useReducer

Can you talk more about more of the tricks you use, with server side rendering and light JS?

What about things like real-time (pushed preferably) updates based upon db changes, or another API finally sending streaming results, etc

Polling is fine for 99% use cases. Even Amazon does this.
What language and/or framework doesnt have this problem?

- Ruby - if your Ruby/Rails app falls behind, you will be paying back debt to get it up to date

- PHP - if your Laravel/CI/Cake/Slim/Symphony App falls behind, you will be paying back debt to get it up to date

- Go - if your Go app falls behind, you will be paying back dept to get it up to date

- Python - if your Django app falls behind, you will be paying back debt to get it up to date

- Elixir - if your Phoenix app falls behind, you will be paying back debt to get it up to date

I have CakePHP code bases that are over a decade old and still run fine.

Go is backwards compatible.

I have years-old Go code that just keeps running. PHP is very stable. I've found Python to be a PITA sometimes. Javascript rots faster than anything else I've encountered.
Of course any system will require maintenance. The question is more about the significance and frequency of the maintenance required.

You can take a Ruby on Rails app from 2005, and it will mostly look the same as a 2023 Rails app. You'll need to update some gems, use the newer routing DSL, and update the environment configs. But that's pretty much it, after nearly 2 decades.

Contrast that to a React app written 5 years ago, where you'd pretty much need to completely rewrite everything (class components => functional, use hooks instead of callback methods, etc.)

A React app can be written in 2023 the same exact way it was written in 2016. You don't need to use hooks. React has literally barely changed at all since its creation. Hooks are a choice. Function components are a choice.
No don't you see, it's their preferred language so its maintenance and churn don't count.
As someone with over a decade of Django experience, _very little_ has changed in Django since it started; you can bring up a very old app to speed in a matter of a couple of days.

I've had to spend a week moving a 3 year-old React app to the latest versions and failed because certain dependencies were just a mess.

Having spend over a decade in Python backend/JS frontend ecosystems at this point, I can confidently say from my experience that Django backends are much easier to update than React frontends. If you import every bleeding-edge nonsense through pip that you can, there can still be problems, but Python has a better culture around this, so it's usually easy to steer teams away from doing this. In JS, importing every bleeding-edge nonsense through npm that you can is the only culture I've found, and this problem is pervasive enough that even guarding your direct dependencies doesn't isolate you from it, since your dependencies are likely to introduce bad dependencies of their own.
"I like python because I picked python."

Literally not one argument here that makes any point.

I've just updated a codebase from Django 3 to 4, about to do another one. It took very little time, with 0 breaking code changes and a mandatory postgres update to 12+.

Even going from pandas 1->2 and celery major version bump (5, I think) caused 0 problems. Mature codebases like these tend to be careful about backwards compatibility.

I have 10+ year old sites on PHP 5 and Laravel 4.x that the client doesn't want to pay to upgrade.
This sentiment makes sense up to the point where you are being glared at by a sweaty PM that can't understand why your foo widget can't be moved to the other side while blinking and ajaxing because HTMX doesn't work like that
Uh... what? You can totally do that easily. Not that you should, but this isn't harder to do in HTMX.
I’m using this approach for a side project and it works great. Just traditional express, tagged template literals (with some xss escaping helpers) and stimulus + turbo. Using vite for bundling and reloading on changes.
I used to do this too. Express + PUG templating. Maybe a little <script> injected VUE on the front end for some light client side stuff.
Yeah, for my personal apps I just write offline-first apps. I need to make it so I can build apps as efficient as I can as I don't have a lot of free time. So, I wrote a little library under 1kB that just finds where I left off and re-centers/focuses there. It makes for dead simple applications.[^1]

I also created an HTMX-like library that is focused on forms so it stays to the HTML speck pretty closely. I need to make a couple of corrections to it. But it is amazing how far it can get you and much smaller footprint than HTMX. Granted, it can only do a fraction of what HTMX can do.[^2]

For anything stateful I just use web components.

[^1]: https://github.com/jon49/mpa-enhancer

[^2]: https://github.com/jon49/htmf

(comment deleted)