Hacker News new | past | comments | ask | show | jobs | submit login
A tale of webpage speed, or throwing away React (solovyov.net)
356 points by todsacerdoti 3 months ago | hide | past | favorite | 300 comments



Most people here are criticizing the author for doing some dumb things. I concur, but still think they have a good point.

First, keep in mind that the author's use case is a content-heavy app with sprinkles of interactivity. This is very important because it sets the "webpage speed" goalpost to a concrete place: they want good lighthouse/first load times and good SEO.

I've fallen into the same pit before. I've used Create React App with a custom server-renderer, Gatsby and Next in different projects. None of the solutions is truly satisfactory for the author's use case for a single very strong reason: React's hydration process is both blocking and slow. I hope that sooner rather than later React is able to offer a good solution for incremental hydration, but it seems quite far for now.

Once you realize this, the only way to keep using React is to step out of the mainstream and play with multiple render roots, parts of the page that never get hydrated and so on. It is possible to do things here, but it is definitely a rocky path.

Of course, there are many wrong things the author explains that you can avoid, but I'll throw a bone to them here too. Most "wrong things to do" they explain are both wrong and understandable. And they openly accept it.

For instnace, one wrong thing to do that I've had to fight against a lot is JS-based device-specific rendering. It is so much easier to implement a "mobile ? <MobileScreen /> : <DesktopScreen />" than to make a single screen that adapts properly using CSS that it's not even funny. Unfortunately, it also breaks SSR, leads to janky page-loads and poor performance.

I fully agree that as of today and for content-heavy sites React pushes you towards a pit of despair instead of a pit of success. You can make it work, but ... is it worth it?


It's just apples and oranges. Web pages should never even consider using React. Maybe a bit of vanilla JS/jQuery here and there for whatever interactivity you need, and then server side rendering for the content. Building web applications on the other hand, has been revolutionized by the use of React. I would never seriously consider any other UI rendering library right now because of the sheer volume of support and active development around it and its' ecosystem. And hand rolling vanilla JS in a large scale web application quickly becomes a maintainability nightmare. The alternative of doing things old school with server side templates is ok, but it massively slows down your iteration speed through reliance on tight coupling between backend and frontend.


> The alternative of doing things old school with server side templates is ok, but it massively slows down your iteration speed through reliance on tight coupling between backend and frontend.

Huh? Something like Rails lets you iterate on server-side stuff extremely quickly. There is a lot of functionality you can implement that doesn't need JS at all


At this point, I think we'll start to see an electron shell that you can install apps/extensions into. The main thing that will prevent this from gaining traction is ios.


+1 to this. I wanted to build a private blogging app/site with a big focus on speed and performance, it might not be popular anymore but I went with Rails. Server-side rendering lets the browser do what it's good at. The only thing that would make pages load faster would be to stick them behind a CDN (which doesn't play nice with the privacy aspect). If anyone is interested I put up a page talking about the project: https://simpleblogs.org


See but the parent comment is about "web applications" not "web pages". You are describing a web page - there is limited or no interactivity, what is there can be reasonably handled by forms. No one doubts that React isn't the tool for this area


Take a look at the hey email client. It's built by basecamp--the guys who created rails.

As for web app vs web page--it's a very blurry line, and it's rare that I end up building something where the entire site could be considered a web app. Usually only some small pieces of it demand enough interactivity to bother with the complexities of using a UI framework, and for that I wrap a small React or a Svelte app in a div and throw it on a page within the larger site.


> No one doubts that React isn't the tool for this area

you would think so, but there are an awful lot of people don't seemed to be stopped by that


Never developed in Rails - does it handle:

* composable templates

* reusable JS snippets

* hot reloading in your browser

And additionally, given that the topic is "building web applications", I can't really understand how you think you can build interactivity without JS. Are you proposing form-based updates or is your understanding of "application" different than GP and mine?


> * hot reloading in your browser

We recently added this "feature" to our react-on-rails codebase, and the only thing I can ask is... why?

Hot reloading on Unreal Engine is a hot mess with all sorts of little caveats to think about. Meanwhile I can hit F5 and as long as my browser is configured right I can guarantee there's no old cruft to deal with. Why in the world would I want to add that kind of uncertainty in my work codebase??


Whenever you’re doing design or interactivity focused tasks, and actually in many cases debugging logic, hot reloading is not only a huge step function improvement, but a categorically different thing.

The analogy I like to make is this: imagine a painter had to wait 3 seconds for every stroke they made to show up. Would they be as good? Would they try out as many variations? Discover new paths they could go because they had the time to “test that weird idea real quick a few times”?

Hot reloading works especially well on React (and not I assume on game engines) because React, with hooks, used algebraic effects, which means all side effects are properly understood by the system and are undoable. So it’s not as hacky at all as you’d imagine.

I never understood the hate for HMR as a concept. Perhaps your implementation wasn’t great, but as a general concept it’s literally a game changer.

The same people who cast shade on it seem to always embrace incremental compilation (like in Rust) for some reason, too.


I guess I can see it in that regard, and knowing the implementation of it is complete and robust helps a lot. And I can see how this is useful for interface design; most of the good IDE gui toolkits of the past preview live as well.

But I also think this lessens the requirement for a solid mind's eye and ability to visualize changes before you make them. Having come from desktop development (with "live" gui development kits like VB) into webdev I guess I got used to code-a-bunch-of-stuff-and-hit-reload pattern.

As long as it doesn't get in my way, I'm cool with it.


Yes.


Can you link me some docs? I would love to see how the modern tools I learned at my outset compare to the classics.


Check out

https://guides.rubyonrails.org/layouts_and_rendering.html#us...

There's no standard way for handling hot reloading, but there's a bunch of recipes on stackoverflow.


The official Rails guides are worth reading and even if you don't want to use Rails are worth checking out as an example if how to do a framework guide well.


[flagged]


As much as your creative sass added to the conversation, I asked a few concrete questions - specifically trying to understand composability, reusability of JS, and what the fellow means by "interactivity" given they claim you can do lots without JS.

They followed it up with a 1-word answer - pretty ridiculous if you ask me - and so I asked an expanding question so I could make my own conclusions of what the other user considers to be acceptable levels of composability and reusability.

While I could undoubtedly find that info myself, I am not the one advocating for Rails here, nor am I the one claiming using Ruby for browser interactivity is a strong idea, so it seems to me like the onus isn't really on me to go search this out


Not constructive.


> The alternative of doing things old school with server side templates is ok, but it massively slows down your iteration speed through reliance on tight coupling between backend and frontend.

This depends on project size. If a single team is working fullstack then using server side templates gives seriously faster product development iteration. Once you get to more than 5-10 devs and you want separate frontend and backend teams then it slows down.


Why can't the server-side template rendering code consume an api as much as frontend JavaScript can? I don't see any reason for this to happen in the browser.


Probably because of how awkward it is. You essentially have this weird html-emitting "client-server" moving part in a limbo where it's still a one-to-many server. And it has to duplicate client code you probably still need anyways in the Javascript that runs in the browser (and, don't you want to do take advantage of some client benefits like being able to work offline or cache locally?). And this bonus moving part is only for your web clients, not something you use for any other client (iOS, Android, etc).

NextJS is something that folds over some of this, but it's not without its issues.

Of course, no better way to find out than try it for yourself. When I have the question of "why don't people just do X?", trying X myself is a quick way to realize why, and unfortunately it's never because I'm the first genius to have thought of it.


Check out Turbolinks which does load and replace the html body from the server and leaves the head content in place, thus preventing the re-evaluation of css and is at each pageload.

It’s backend-agnostic.

https://github.com/turbolinks/turbolinks


The first time I got even close to a custom web page (internal request system for our team), the devs did this. They wrote it as two Bottle (python) applications: One was the API for the app, the other was the delivery of the Javascript to the client, which called the API.


the template rendering lives side by side with the controllers, why would it make an api call?

But something similar but not the same as that was the norm during the jquery era before the early js frameworks arised (backbone, ember et all). You had your full-stack server-side MVC framework render initial views and from there the js would pick up and all UI interactivity would be ajax calls to the restful(ish) api. It was pretty terrible.


> why would it make an api call

Because this introduces clear separation of concerns? You don't need to make this API call via HTTP, you can just call a function. You don't even need to generate/parse JSON!

I know that because this is what we are doing. Just calling a function called "httpapp". :)


Well, but then it's not the same api call and that's the point: that in the old mvc frameworks it was painful having to support controllers that both rendered html and json for doing both SSR and Ajax.

Around the time the first js FE frameworks came out, people finally became confident enough to have pure js clients and only json APIs.

And finally gatsby, next.js, etc. brought a new twist to the SSR and Ajax api combo.


This all indicates that browsers and html/css are bad tools for creating UI.

Long term I think we'll eventually see the ability to interact with the local system via the browser, and we'll start seeing more things like "QT for the web".

Because if we don't, at some point it's all going to just fall over.


Yes. HTML is a terrible platform for applications. It provides basically nothing and I am continually surprised that it doesn’t seem to be a major focus for improvement like JS and CSS.

I don’t need more types of semantic rectangle, I need actual real UI controls that are efficiently rendered and accessible by default, so I don’t have to build everything from scratch.

The fact that there’s no built-in element for things like dropdown menus is mind boggling to me. That’s GUI component #0; menus have existed for as long as GUIs have!

I feel that Houdini may eventually solve CSS’ shortcomings for application UI layout, but being stuck with HTML is like trying to build an aeroplane with sticks and mud.


>The fact that there’s no built-in element for things like dropdown menus is mind boggling to me.

You mean the select tag?


No, I mean like a pull down menu. Like when you click “File” or “Edit” on $desktopOS



Qt for the web already exists within Qt.


That is not what I'm referring to, you're concentrating too hard on the specific example rather than the idea behind it.


It is also a frustrating experience to endure reloads on every click if what the user is using feels like a web application and you're essentially placing that burden of reducing the interaction roundtrip time on your infrastructure which gets expensive really really fast.

Most web-thingies are neither 100% plain websites nor 100% plain webapps, most are in the middle, some are heavily leaning on one side or the other.


You don’t need even build always a SPA with React. For interactive widgets, Resct works better to me than jQuery.


And who decides which is which? You?


These days nothing is purely static and very little is purely interactive/dynamic. However most sites lean clearly to one side or the other, no judgment call or personal defensiveness required.


Most sites straddle the muddy line in-between, or start off as webpages and mutate into web-apps over time as more functionality gets added on.


I stop reading the webapp/react specific posts on HN for a year, and now there's such foreign terminology that I can't even understand the comments of one. Hydration? From context I assume it's something to do with rendering trees and the Dom, but really, I'm just grasping.

So out of hand. It feels like Javascript webdev is recursively devouring itself into a completely separate type of programming.


hydrate [1] is the API call by which you tell React to initialize itself against a pre-rerendered DOM. In contrast, render [2] is the call by which you tell react to generate the DOM nodes itself.

The idea is not that complicated:

1. You send the page's full HTML to the client. This is good for SEO and to quickly get the page to show. This HTML has been generated by running React on the server and capturing the output.

2. You send the React stuff (React itself, your pages components, extra libraries you are using, etc.)

3. You hydrate React's virtual dom using the already existing DOM (that the browser has created in step 1). This essentially amounts to telling react to attach the proper event handlers to the DOM so it can continue working as if that DOM was created by React itself.

[1] https://reactjs.org/docs/react-dom.html#hydrate

[2] https://reactjs.org/docs/react-dom.html#render


Thanks, and yeah, I got most of that from some Googling around afterwards, but I do want to note that I had encountered your first link, and that it itself is somewhat recursively defined with terminology that is glossed over:

ReactDOM.hydrate(element, container[, callback])

Same as render(), but is used to hydrate a container whose HTML contents were rendered by ReactDOMServer. React will attempt to attach event listeners to the existing markup.

Searching for hydrate in the React docs leads you to the hydrate method, which describes itself in terms of its use to hydrate a container. The crazy thing is that there's numerous blog posts that purport to tell you what react hydration is, which do the exact same thing and define it in terms of itself. Partly, this may be React (and the community's) reliance on you knowing what that concept is, but that's particularly unacceptable in article that purport to explain it.

Is it really so hard for these reference docs and explanatory sources to say something along the lines of "hydration in React is the act of fleshing out a server delivered model of the page that came with poor or no data with rich data delivered later in the process" ?


"Hydration" is a term often used on the backend too. I believe I have noticed it for the first time in Doctrine documentation, Object Relational Mapper (ORM), which is similar to Hibernate in Java - it was about transforming data retrieved from SQL database (memory) into usable PHP structure (object) [0]. In my opinion, this term has been perfectly described on the StackOverflow [1].

[0]: https://www.doctrine-project.org/projects/doctrine-phpcr-odm...

[1]: https://stackoverflow.com/a/6991192


Yeah, I ran across that StackOverflow when I finally removed "react" from my search term when first looking.

I will say, hydration in the context of an ORM seems fairly odd to me. My concept of ORMs doesn't mesh well with the need to pre-populate of bunch of template which you fill the data in for, since I think of them as pretty much all data with some behaviorioral added on.

I guess it could apply towards auto-fetching data as you traverse relations, but those don't exist in a skeletal form AIUI, they are created as needed when requested and data is queried (that is, there's no structure to hydrate with data after the data comes back). I guess you could create objects, query data, and then add data to objects, but given that you often don't know how many objects you'll need, I'm not sure how beneficial that is.

Then again, it's not like I have enough experience with the concept to know the nuances of how it's bandied about.


Wow, hello quantumly-entangled developer twin. All this Gatsby, Next, React hydration, multiple roots stuff is what I'm traversing at the moment. Even down to the `<MobileScreen /><DesktopScreen />` example. I'm having discussions about these things almost every day. And then to finish it off, you mention the pit of success which has been a key focus of my recent attention too. I could imagine myself writing this exact post, even down to the word 'janky'. I had to check to see if you were a colleague of mine, but I doubt you are.

Since we're tackling similar problems, do get in touch with me if you want someone to share ideas with in future.


When it comes to the mobile-desktop thing, check out Rebass, which uses actual generated CSS under the hood so it's properly performant (especially if you handle it right by precompiling all the static CSS at build time with babel-plugin-emotion), but lets you write media query-dependent attributes directly with your component use. For an example: https://rebassjs.org/box and resize the page back and forth


Kind of off topic but when the word "hydration" become a technology term? I see it all over and it's never really made sense to me. The definition is specific to fluid:

1 : to cause to take up or combine with water or the elements of water 2 : to supply with ample fluid or moisture

Maybe I'm old school but the word "populate" makes a lot more sense than hydrate when talking about data.


think powdered milk. You hydrate it by adding water.

Same idea, only for objects. You hydrate them by adding data.

The term has been used for probably 20+ years.


There's an interesting correspondence with water and data going even further back than that in the English language, think about how often you hear variations on "learning through osmosis" or "hopefully I will pick that up through osmosis". Osmosis is specifically diffusion of water. It's fascinating that so many speakers of English refer to it as "learning through osmosis" rather than the more accurate and less water-centric "diffusive learning".

I've no idea of the etymology for this or why this conflation is surprisingly so deep in contemporary English culture, I've just been fascinated by it for a long time.


This threw me straight back to secondary school, we had a teacher with a fearsome reputation who was berating a class of mine for not revising efficiently, he said "you can't just open your book and learn by osmosis" and some very brave kid piped up with "it's not osmosis sir, that's the movement of water across a membrane. We'd be learning by diffusion, not osmosis". At the very least, this proved we'd been paying attention in biology!


Not sure diffusion is really more accurate than osmosis. It does need to diffuse across the brain membrane, after all ;)


Well, one can assume the eyeballs are akin to an aqueous membrane, but the human ears generally should be non-aqueous for best results. ;)


Over 10 years ago with various ORMs.


React supporting selective hydration would potentially be a total game changer.

I have recently started using gatsby-plugin-no-javascript. It's a very crude version by removing hydration at the page level. It works pretty well for me because my site is a ton of static pages and then one very interactive app page. I get to build it all in React, get a great developer experience, and then all those static pages are like 20k total (css, images, html, everything) and load in the browser instantaneously.

If we could get this ability, but be able to apply it at a finer granularity than pages, we could probably do some really great things.


> gatsby-plugin-no-javascript

Thank you for mentioning this! I’ve been on a passive search for exactly this: the ability to use React and generate a completely static site without even the possibility of JS at runtime.


I've only used Vue SSR / Nuxt this way, so there might be a fundamental difference in the implementation, but hydration process in it isn't blocking, and whether or not it's slow greatly depends on how heavy your webapp is.

The page that he browser is hit with has ALL the content pre-rendered (sans CSS) in that first HTTP HTML response, and it behaves like a classical webpage henceforth until the moment that the interactivity bits are being hydrated which is deferred.

I concur that it's still a bad idea to use SSR SPAs for use-cases where a non-dynamic HTML page would work decently well, and there are ways to pack some of these frameworks (Vue in particular) so that it's a JS dependency of an otherwise functional webste (i.e. progressive degradation) for a lot of the in-between use-cases, but when what you're building is heavily leaning towards being essentially a web application (say, an e-commerce site, webmail client etc) then SSR is certainly the most viable option for a decent user experience that isn't full reloads on every click, but also doesn't take ages to become interactive.


> The page that he browser is hit with has ALL the content pre-rendered (sans CSS) in that first HTTP HTML response, and it behaves like a classical webpage henceforth until the moment that the interactivity bits are being hydrated which is deferred.

That is what Gatsby / Next.js do too. The issue is that deferring the hydration doesn't mean it is neither non-blocking nor quick. Once hydration is triggered, the browser gets blocked until it finishes. Of course, the speed of the process depends on the site and how much content it has. If you are building an e-commerce site, this will tend to be on the side of heavy (just the full menu structure, footers and such will be quite a lot already).

> but also doesn't take ages to become interactive.

The issue is that a regular old website can be interactive almost immediately. If you have a hydration process interactivity is inevitably delayed for any part that does require javascript to function... and also for the parts that don't, because the browser main thread is blocked for a while doing the hydration and won't respond to your inputs until it has finished.

If you don't believe me, open an incognito chrome window without any active extension, go to nuxt's own documentation site [1], run a lighthouse performance evaluation with the default settings (mobile/simulated) and see what scores you get.

In my laptop it is a 33 overall for performance, with 4.1s FCP, 9.2s TTI, 6.1s LCP and a total blocking time of 2.41s.

You can also test from https://web.dev/measure/, where I'm seeing a 57 overall (much better, but not good) with 3.5s FCP, 7.8s TTI, 4.9s LCP and a total blocking time of 500ms.

That is, the creators of this software have built a documentation site (i.e.: mostly text, very little interactivity) that doesn't get good performance scores. This, along with blogs, is the best use-case I can think of for the technology. And it doesn't perform good (according to Google's-defined objective metrics, not mine!).

[1] https://nuxtjs.org/guides/get-started/installation


Vue’s hydration is much better, partially because the templating system allows more of the pages to be rendered. I’ve had react apps that had various convoluted systems that ended up rendering as basically blank pages before hydration due to dynamic state based on cookies, etc.


If you're truly having issues with CSS and rendering different sizes, I'd recommend taking the time to really learn CSS inside and out. I find it incredibly simple nowadays to do both Mobile/Desktop (+Tablet) with CSS and media queries. I may have agreed with you in the past, but not so much anymore.


There are still some things that are impossible to achieve with CSS alone, even if you have the luxury of only needing to work with evergreen browsers (remember we are speaking of content-heavy sites here, which are the most prone to still have to support older browsers).

Example: I have a stack of boxes of varying heights. On mobile they are fine as is (one below the other). On tablet I want them in two columns but displayed in column order without gaps. On desktops I want three columns. This is dynamic data, so the number of items and their heights will vary, but my CMS allows the user to mark at which box(es) new columns should start:

  mobile  tablet  desktop
  a       a  c'   a  b* d*
  b*      b  d       c
  c'
  d*
' indicates this element starts a new column for tablets (two-column layout)

* indicates the element starts a new column for desktops (three-column layout)

Can you achieve this with HTML/CSS alone? How?

- CSS columns won't work because Safari doesn't support break-brefore in a columns context.

- Flexbox won't work because you don't have a defined height for the container (so you can't use flex-direction: column + flex-wrap).

- CSS grid won't work because you'll get a grid (i.e.: gaps between elements when the boxes have varying heights).

- Ye-olde-floats won't work because you'll get weird gaps too when the sizes change.

- You cannot wrap the elements in column-divs because you can't do it for both tablet and desktop simultaneously.

Also, you need to be very confident in all of the above to know that is is not possible, instead of getting sucked into trying, getting to an "almost there" point and then realizing it doesn't work under X condition.

In contrast, with React you can just generate 1/2/3 wrapper divs depending on whether the current width is mobile/tablet/desktop and call it a day. It takes you all of 5 minutes to do so and the margin for surprises is 0.

Edit: reworded why flex won't work to address nawgz's comment.


I would tend to use grid for this, and no, I wouldn't use HTML/CSS alone, if there's a dynamic dataset. The intent with my comment is you don't need to render different views entirely, but that there are CSS tools (col-span-1, 2, ... ) + grid that work really well.

That said, I also don't have to support older browsers. We're content heavy, but traffic is so small from Safari, older IE, etc. so we're fine with using newer features.


What about 'order' attribute?

https://developer.mozilla.org/en-US/docs/Web/CSS/order

Also if css can't do it why go you need react when you can do it in few lines of css? Not not difficult to rearrange elements based on viewport size.


>Flexbox won't work because you don't have a fixed height

Are you saying the issue is that flexbox will force everything to have the same height? I am relatively sure that `flex: 0 0 fit-content;` on the children and `flex-wrap: wrap; flex-direction: column;` on the parent will do what you want


> Are you saying the issue is that flexbox will force everything to have the same height?

No, I'm saying that you will not get columns unless you specify a fixed height for the container. Since you don't know the total height you want for the container, this is not a valid solution.


Ah yeah, I see your point. Fair enough.

However, if you're already using wrapper divs, it seems to me like you do some math on your boxes after the initial render to balance them. Either that or you literally just group them into 3 groups and accept whatever artifacts (read: column height mismatches).

Either way, there is a CSS-only solution of sorts. Render the parent with 0 height, iterate over the children groups to find your max column height (remember - you already do this grouping with your wrapper divs), set your component height to your max of the 3 groups with 200ms transition, and you have a sweet CSS-only animated solution

Might not be worth it - in my approximation it's not - but I guess that could depend on your domain.


Hmm, why did the CMS allow for such a setting in the first place? It seems to me that allowing this is the problem... But this might be a structural/org-political that you have to solve then


If you don't have to build a webapp just use Webflow


The initial motivation for intercooler.js (which the author forked) was performance. I was working on a large bulk table update and building the table dynamically in javascript. The performance was terrible (this was back in 2012, no idea what it would be like today).

I realized that I could just deliver and slam HTML into the DOM and that the browser engine, written in C, was very fast at rendering it.

That turned into a pretty big javascript function, which then turned into intercooler, which then turned into htmx:

https://htmx.org


This is everything I never knew I needed.

At a glance through the docs, I'm pretty sure I can replace about 30% of my website's javascript codebase with this. Not to mention that having to actually write the javascript to "spruce" up a form will often lead me to be lazy and just have people deal with an un-spruced-up form.

I'm itching to get off work and try it out for real :) Thank you for all the work you did on this.


I could definitely see something like intercooler boosting productivity. My only concern (possibly unfounded) is that you end up designing all of your server side endpoints specifically in an intercooler fashion. All of your endpoints must now return an html snippet, which is specific to the design of the page. So you have a lot of page specific endpoints. This is in contrast to a REST api where the api can be designed largely independently of any one use case.

If you ever need to switch away from intercooler you are going to have a large undertaking not just on the front end, but now on the back end as well.

But I might be missing something.


If you don't need a rest API, all it does is make everything far more complicated, i.e. every page now has two end-points, one for the html, one for the data.

It's trivial in most web frameworks to have an endpoint respond in two ways, one with all the html including head, menus, footers, etc. if you hit it with a GET, the other just the snippet if you hit it with an Ajax request.

A REST API is basically a massive over-complication unless you actually need it for a good reason, say you're running both a web app and a mobile app from it.

I've used this technique occasionally for over a decade and personally have always found this server-side approach very simple compared to juggling REST APIs with client-side rendering when a client or an existing code base demanded it.

I've also always found the defence 'you might need to switch' to be a flimsy one. Usually when you do need to switch, everything is so different even your 'future-proof' API design needs a massive overhaul too because you made assumptions you didn't even realize you were making.

Think of all those SOAP or XML APIs that were future proof...


Fair points. A couple of notes though - intercooler needs two endpoints as well. One for the page, and another for any dynamic HTML.

I mean I switched from jQuery to knockoutjs to react all on one application and the API served all those transitions well. So I'm speaking from personal experience here. But that is anecdotal and maybe it's not typical.


One pattern that I have used with some success is to reuse end points and use metadata that comes up with intercooler or htmx requests to determine the structure of the output.

For example, if I have search functionality at

/search

and I'm implementing the active search pattern shown here:

https://htmx.org/examples/active-search/

I'll re-use the /search url for the partial search results and check the HX-Request header to determine if I want to render the entire search UI or just the search results.

If you use hx-push-url as well, you can get a search dialog that acts like an active search for the user, but also retains copy-and-paste-able URLs


Just adding some additional commentary based on your post.

I think you're talking about the difference between an "experience API" - that is, an API with the sole purpose of being support for user experiences/clients - and a "system" or "process" API, where the latter is for application or process integration between many systems. These are terms borrowed from Mulesoft, but I do like the terminology, I find it helpful for segregating concerns.

There are a lot of reasons people need separate experience APIs to power specialized UI/UX - especially with the needs of different client platforms (ex. chat bots vs phones vs desktop browser), separate from system/process APIs.


Yeah, I would recommend adopting htmx (or intercooler) incrementally, where it adds the most value. And when it doesn't "feel right" for a particular use case, don't use it there. This minimizes your commitment to the approach and lets you use the right tool for whatever UX job you have at hand.

EDIT: I should have read your comment more closely. With respect to two end points, one html and one JSON: I view the JSON and HTML end points as separate problems that both benefit from not being conflated with one another.

Your HTML end points are tuned to the particular use cases for your UX (e.g. active search) with the caching and tuning required for your specific needs.

The JSON end points need to be general and support unknown 3rd party client needs, and thus require more expressivity (e.g. GraphQL) at the cost of not being tuned for particular use cases.

I tried to get this idea across in this older blog post:

http://intercoolerjs.org/2016/01/18/rescuing-rest.html


> All of your endpoints must now return an html snippet, which is specific to the design of the page. So you have a lot of page specific endpoints.

This is ok, adding an endpoint to spit out html is super simple, it's just printf statements with angle brackets.

Beside that I find endpoints need to be somewhat coupled to the UI anyway, otherwise the endpoint needs to be a superset of all possible data and all the complications that come with that.


When you migrate away from intercooler the new front-end is loading some data-api and some html-blocks from the old way. Move them out in increments.

Intercooler is great for some cases and can make a good transition step moving from all server-side v1 to a fully responsive/dynamic v3


I love seeing more work in this space! This sounds similar to the approach Basecamp has taken, with tools like stimulus.js (combined with with Rails UJS and Turbolinks). You can make a really responsive page with normal server-side HTML templates that "sprinkle" in the ajax functionality.

Tools like this feel very familiar to those of us who got started before the rise of the modern JS framework. It's kind of fun to see articles like the OP's pop up where people are rediscovering these techniques.


Yep.

The problem is that HTML was never completed as a hypertext, they just kinda stopped at anchor tags and forms.

There isn't a good reason that only anchors and forms should be able to specify HTTP requests. There isn't a good reason that only clicks or form submits should be able to trigger HTTP requests. There isn't a good reason that only POST and GET should be readily available (and POST only for forms.) And there isn't a good reason you should have to replace the whole page on every HTTP request, rather than a component within it.

htmx is an attempt to complete HTML as a hypertext.


    There isn't a good reason that only anchors and forms 
    should be able to specify HTTP requests. There isn't a 
    good reason that only clicks or form submits should be 
    able to trigger HTTP requests. There isn't a good reason 
    that only POST and GET should be readily available (and 
    POST only for forms.)
That's an excellent and thought-provoking way to think about it.

I'd always been mentally locked into HTML's basic "the web is a series of linked pages" paradigm that was in effect ever since it debuted, thinking that to do anything outside of that paradigm you'd obviously want to resort to manipulating the DOM directly with javascript.

But, there's really no reason for responsibilities to be divided in quite that manner. There's really no reason HTML itself can't encompass somewhat more robust hypertext features, with declarative support for functionality like "this link should load URI abc in the xyz region of the current page."

Frames, of course, did sort of do that natively in HTML, but that was a very clunky implementation to put it mildly.

I can think of potential arguments against what you say, but I think I agree...


Right. And not only is there no reason for responsibilities to be divided that way, as it stands you kneecap the promise of REST/HATEOAS by restricting it to the very specific cases of anchors and forms.

So with htmx I'm trying to complete the HTML hypertext and let people take advantage of the simplicity of the REST model without sacrificing user experience.


Yes. Unfortunately none of the Standard and Browser Vendor has any interest in improving it.


http://quickenloans.github.io/Behaviors.js/

Once I got past certain mental blocks, it became fairly obvious that you can structure all your GUI scripts to be configured and to interact with one another via DOM.

The next insight was to use CSS selectors for targeting.

Then using consistent name prefixes and separating behaviors into self-contained libraries.

The stuff above was proof-of-concept. The possibilities behind this approach are mostly unexplored.


I love seeing people invest time in this area, kudos. One thing that’s a bit hard for me to justify though are the examples like “click to edit”. There is a very noticeable delay as you wait for the network request with the edit document, whereas you typically won’t see that with client-side view logic. Is this just not a problem that htmx is trying to solve?


That response will be as fast as your server is, which is typically fast.

I stuck a 300ms delay in the mock server to make it seem a little less instant:

view-source:https://htmx.org/js/demo.js

I didn't want people to think I was misleading them about what was going on. I would typically expect a simple edit form to return in sub 50ms.


> That response will be as fast as your server is, which is typically fast.

I do agree here. Also, if you’re careful about it it would be really easy to just scale the shit out of the document builder, add caching, etc.

Looking forward to following your project, it’s very interesting to me!


This is really interesting. Thanks for all the effort you've put into this project.

I had one quick question - how easy/difficult would it be to integrate another JS library with intercooler or htmlx. For example, let's say a table is fetched dynamically via htmlx, how would we go about integrating a library that does client-side table sorting/filtering?


It's not perfect, but not awful either. Basically you have to catch the content and initialize it with the library using a hook:

htmx.onLoad(function(content){myJSLib.init(content)})


Wow, that's awesome! Would have definitely made some stuff I've done a lot easier.


Is it possible to make components like Dropdown with autocomplete in this? Should we be making use of Extensions for that?


It isn't really component focused, but there are patterns like active search:

https://htmx.org/examples/active-search/

Htmx can be used to implement reactive components, however. Ben Croker created the Sprig component framework based on it for Craft CMS, for example:

https://putyourlightson.com/plugins/sprig


This is amazing.


This is the typical "we didn't spend any time thinking about our architecture therefore we're going to blame our framework" article.

React is a great choice for certain use-cases, but when low-quality developers are allowed to pick it up and apply it to everything you end up in a mess. The same thing happens with literally any tool.

If you want speedy initial interaction times and manageable codebases, (and requirement X) use the right tools for the job, and instil better, thoughtful, development culture.


In general, these posts sound a bit like this: I tried jQuery, and after a while it all became a mess; took up a Backbone project, and was good for a while, but eventually it became too complex; then I worked on Angular and that seemed a big improvement, but then... and finally with React my architectures are clean.

While the reality is more like this: I had 6 months of programming experience and used jQuery and made a disaster; with 1.5 years of experience I used Backbone and I fared better; with 3 years of experience I tried Angular and I was able to build a decent size application but ultimately shot myself in the foot; and now that I have 6 years of experience my software quality has improved a lot, it must be react!


Pretty much. It's similar to discovering the power of salt and pepper in cooking. When you first use it everything tastes better. But the next step is not to increase the amounts you use in every dish, it's to explore the much wider world of cooking with herbs/spices/etc.

If your mindset is persistently "this framework/tool will solve all our problems" you're always going to have a bad time. Understanding the pros/cons of each element is essential to becoming a good developer.


It's almost like all "modern JS" are written by new programmers hired on the cheap by companies to work on their new hip UI frontends.

IMO UI is generally something new programmers like because of the visual/visceral "I built that", but once you get exposed to the sheer annoyance of UIs, programmers will migrate to backend.

So the most experienced people don't want to be constantly undercut in price by the incoming "talent", realize that WebUIs get chucked every 3-5 years anyway due to browser tech churn, and move to data monopolization.


This can be generalised to all software that is perceived to be revolutionary. Sooner or later a big company that wants to retain and attract engineering talents will arrive, with a horde or junior engineers led by an engineer looking to justify a promotion or to embellish a CV.

API generators had the same thing. Strongloop was a decent frameworks before they started throwing 10'000 juniors to fix issues and made a mess of the codebase. If you look at the jungle the React codebase is and you compare it with Preact (it's smart, concise and performant) you'll understand why code quality matters. And I'm not talking about stupid metrics.


The JavaScript world changes so fast that no one is ever going to be very experienced in the the they are using. I imagine that is partially to blame for all the crap we see in the front end.


I agree, the JS ecosystem is reflective of constant new programmers redoing the UI every couple years, although I need to grit my teeth and admit that React did at least standardize/structure the ecosystem for the last few years.


I’d just like to expand on this, as I think it’s a great comparison!

Carefully picking tools is important, but also, don’t adopt more tools than you need to.

One common example I have seen is pulling in a CSS-in-JS library to do something that SASS can easily do... when SASS is already incorporated into the build process.

SASS offers a fairly complex set of features if you care to learn about them, and the module system (in development) is going to solve @import global scoping issues very elegantly.


If you need something much less complex than Sass, but has a similar syntax to Sass (not SCSS), PostCSS + SugarSS has been nice for me. I really prefer indented syntax, and ever since CSS variables became readily available on my project support requirements, there's not been much reason to reach for Sass. A lot of people seem to point to the color functions, but I don't think a lot of people noticed Sass and Less don't do color mixing/darkening properly (they don't square the gamma before doing operations) so I wouldn't advise using these built-ins anyhow.


I would change it a bit and suggest that after angular, they did react, and after 18 months of that it was too convoluted, but 'hooks' solved everything, and then 12-18 months after that, things are just way too complex, and they're now investigating svelte or something else.

I rarely see any decent architecture survive growth and real world use beyond a couple years. It's usually either 1) "no one could possibly have foreseen this new use case/requirement" (from less experienced folks) or 2) "YAGNI!" when trying to build in some abstraction levels to handle use cases you know will happen down the road.


In reality these often become the scapegoat for a need to refactor. I know refactor is a four letter word to some, but the truth is we grow as developers and architects and learn from our mistakes.

There's a tendency to blame the tools. You can build a big, high quality app in almost any language and framework.


I disagree with the reality you present. There are a lot of people, myself included, with a considerable amount of experience who found that maintaining jQuery/Backbone code to be complex and error-prone and React presented a solution to this problem with a programming model that looks more like an immediate mode GUI (even though it ultimately paints to a retained mode DOM).

I don't think it's worthwhile to minimize the actual, useful impact React has had for web apps.


React has been god sent for us as we slowly modernise an application that is a mess of server rendered html and hacked together frontend code.

Slowly we are rewriting individual pieces as embedded React components (no SPA here) and moving to a proper API layer that the components talk to. The separation of concern has made it a loot easier to increase code coverage and ensure a controlled rollout of new features.

We also just bit the bullet and paid for syncfusion to use on our frontend to avoid reinventing the wheel for a lot of the functionality we need.


> The separation of concern has made it a loot easier to increase code coverage and ensure a controlled rollout of new features.

This can be achieved (and more easily) by separating your templating from your business logic at the package level. If you know what you're doing you can keep things separate and your import graph non-cyclical, if you don't know what you're doing you're going to recreate the mess in your components and API anyway.

Source: Tired of seeing this happen again and again and again and again. And fixing it.


Everything done badly of course causes the same effect. But we are dealing with a massive mess that is the worst case of both worlds.

Since we need to also provide an API going forward to our clients, for our usecase this was a great solution. However we are not doing an SPA, the main framework and navigation is still server driven. The difference is that the "create new user dialog" is now a react component that calls the corresponding api.

Running through the API also makes it easier for us to handle the caching of data at that layer instead of a mix of jquery calls and random div HTML generation.


Honest question. As someone whose only worked with open source tech in React such as Bootstrap, Semantic UI, Material UI, etc, what is the advantage of a paid UI framework like this?

Maybe stability? Besides Semantic UI I found that other frameworks are nowhere near as mature. I guess if you're a big company its a small price to pay but it also seems like you're paying a grand + per month for something with tons of free alternatives.


You're paying for the support you need when your developer can't fit requirement X into the component, or when they run into a bug, or whatever other reason.


700 usd a year for the library for a developer. It saves us a ton of time as its a B2B business and we use a lot of stuff like a calendar view, gantt diagrams and scheduling. Rewriting it from scratch of adapting a random set of components would be more expensive in time and resources.


godsend


IMO it’s unfair to blame “low quality developers”. The industry has coalesced around React as the one stop answer to everything. Coding boot camps focus on it to the detriment of broader web technologies. It’s worth calling out when it actually fit a use case.


> we’ve discovered that React also leads to some questionable practices. Like hovers in JS (rather than in CSS), drop-down menus in JS, not rendering hidden (under a hover) text (Google won’t be happy), weird complex logic (since it’s possible!), etc.

This is some strange reasoning that I have yet to seen myself. If you can do the hovers states/drop down menus in CSS, why not do them in CSS, even if you're using React? Seems to be blaming something on a library that the library has no care about in the first place (which to be frank, seems relatively common in web dev circles).

> In the worst case, we would serve you 2.5MB of minified (non-gzipped) JS

And holy guacamoly, how do you end up with this?! Seems that something was surely wrong in the compilation options, forgetting to mangle names or something, missing dead-tree elimination maybe?


Talking of bundle sizes, this isn't inherent to the React point being made here but it's arguably too easy to blow up what you're serving. A common example I like to reference is Moment[1] — unpacked size is 4MB, most of which is different locales. If you _don't_ want to bundle all of those with your project, you have to do extra work, instead of making locales opt-in. Perhaps slightly less used, but a more prominent example, a widely-used table component Ag-grid[2] is 25MB before being packed down. Instead of being served as a modular set of components, it's mostly a big blob with minimal separation of concerns.

The takeaway here is twofold: we need better tooling as well as better practices for how to use those tools. It's both an educational problem as well as a toolset issue. It is possible to build lean and fast pages, but it's currently considerably harder than building gargantuan monsters. I don't see the web bloat problem going away until the dynamic here is flipped — it should be easy to ship small bundles even with minimal experience.

[1] https://www.npmjs.com/package/moment

[2] https://www.npmjs.com/package/ag-grid-community


At my last job I always tried to be quite strict about adding additional dependencies, if something was over 10kb it had to be very strongly justified.

Our designers wanted to add some SVG animations, they started off with Lottie (https://bundlephobia.com/result?p=lottie-web@5.7.2) because they could export directly from After Effects, but I said no chance because of its file size. Briefly considered Greensock (https://bundlephobia.com/result?p=gsap@3.5.0), but it was also too big for the kind of animations we were looking it.

I'll need to double check what we ended up with, but it didn't have any library code, so each animation was just a self-contained bundle of SVG and CSS animation code, and fairly small.

Edit: I asked around and SVGator (https://www.svgator.com/) is what we ended up using.


Too many people don't respect the risk of dependencies.


Tangentially related; is there no lint option (similar to use strict) in JS yet to ensure no methods / functions are dynamically called and thus allow effective dead code elimination?


Not in vanilla JS. If you use the flavor of JS accepted by google’s closure compiler, then yes.


If anything, it proves how ineffective standards committees are. Similar to the standard library proposal which seems to be not yet implemented.

Still better than C++ standard people who try to cram infinite complexity into 100 layers of templates every three years for sake of adding a feature as library instead of language feature, with utter ignorance towards debug build performance, compile times and error messages. I have lost respect towards them since I read a C++ performance report and didn't see the mention of inherent STL inefficiencies.


That first quote (using JS when CSS would be faster and simpler) does remind me of being a beginner and always arriving at jQuery's animate() for all my needs.

That said, animation is hard, and React doesn't insulate you from that, so it takes some thought. In fact, being a layer of abstraction, it requires even more understanding of animation. Fading a component in is easy with regular CSS transitions. But fading a component out generically means that you have to keep the component mounted for the transition.

Perhaps anyone who has used react-bootstrap's Transition system (animate={true} on modals and tooltips) has run into those quirks where you start seeing a pattern of animate={false} fixing all sorts of random bugs.

With React, if you're grabby with 3rd-party libraries, you can end up with a russian doll of HoCs, each one from a different library, that are so generic that they kill performance and it's not even obvious why without being a profiler expert. Whereas without React, you wouldn't have found those solutions at all, so you roll your own cross-cutting solution.

That isn't necessarily a problem with React, but something that you have to resist with frameworks in general. It reminds me of Ruby on Rails, googling "rails avatars" and ending up with two libraries like carrierwave + "has_avatar" when you could have just built your own simple solution. It's a form of technical debt. After all, it's hard to justify the effort of deabstraction when you're assigned to 100 other issues.


RE: jQuery.animate() - that was around before CSS animations were. Animations were not in the CSS spec until v3 which only saw widespread support this side of 2010.

Before then you could only do animations with Javascript, and frankly jQuery's `animate()` was a god-send.


On top of that, js animations are still better for controlling timing and events or for a more realistic natural motion (springs instead of timed easing).


I would say that what JS animations are even better at is justifying inflated budgets of the projects made for people who are not very familiar with tech.

Is there ever a real reason for animations that doesn't make the interface feel sluggish and unresponsive? In the other hand if you actually set the animation delay to something incredibly low, all of your animation needs could be easily solved by animating the transparency of the appearing elements.


Animations are effective, and I say that as someone who really hate slow ones.

If you start paying more attention to the apps you use, especially ones made by bigger companies, and you'll notice how effective some of them can be.


Is the latest version of Google Calendar web interface a good example?

I don't think it is. And it's from a company which can afford a lot of development resources for that.


You can use it to make your UI more obvious. If you hit "Delete", the item might shrink and slide (within say 200ms) into the "trash can". This way the user knows they can undelete from the trash can.


> Seems to be blaming something on a library that the library has no care about in the first place

Libraries and frameworks establish idioms, which encourage or discourage certain patterns. In my experience React / JSX definitely encourage complexity and abstraction by making display and logic so intertwined, especially with hooks.

> And holy guacamoly, how do you end up with this?

Libraries upon libraries, one tiny problem at a time. Unless you're in a very small team, or have very strict policies for adding new dependencies and vetting their impact on bundle size, this will inevitably happen.


I can second this. I'm working on two projects, one paid and one volunteer. One is using vanilla css/html (requirement to work without js) and the other is using react. Doesn't matter which is which.

To set the context for the following statement, I've discovered that I prefer developing the nojs site. This mainly comes down to all the things that the browser does for me, but I have to handle manually in React, but it also includes something fun about figuring out the right css selectors to accomplish pseudo-interactivity without js. The point here is that I'm not personally inclined to avoid css.

And yet, when I work in react, I find myself writing less css, and the css I do write is closer to utility classes, something I never used in the nojs project.

I'm writing this on mobile but it's getting too long, posting and will move to desktop and edit in the reasons why I think React pushes me in the direction.

e: got here just as the edit window is closing, will reply to self instead.


The first reason is what ricardobeat mentioned — with JSX, I'm writing my display "html-ish" code at the same time as the business logic. If I'm managing the state of, say, a form, and I want the submit button to be disabled when (condition), the obvious solution is to add `disabled={condition}`, rather than a conditional className or attribute to then match on in css. When I'm doing everything in one file, switching to a different file feels out of place and breaks me out of my normal flow.

This is also why I tend to think in terms of utility classes. When I'm writing JSX, I'm also visualizing how the final page will look (or actually looking at it, side-by-side). So, it's natural to be thinking about the actual styles I want to apply, not classes that hold several related styles. Sometimes I'm almost tempted to write inline styles, but thankfully the syntax is awkward enough to dissuade me.

The second reason has to do with how the html part of JSX is structured. When writing css for plain html, it's easy enough to see how the page is structured and go through it writing css classes for each block (or applying existing classes). Then you refactor as you notice common patterns and to put different style in the right places (oops, that margin should have been padding on the parent element…), like with any code. But with React, js is often changing bits and pieces of the dom itself, so it can be hard to look at and see the structure in the same way. Thus, it's easier just to apply the styles in the same place as everything else. (In React's defense here, I think if I were doing a similar amount of dom mutation using vanilla js, jquery, etc, it would be even harder to write css for it… but on the other hand, vanilla js doesn't encourage me to do nearly as much of it in the first place.)


> If I'm managing the state of, say, a form, and I want the submit button to be disabled when (condition), the obvious solution is to add `disabled={condition}`, rather than a conditional className or attribute to then match on in css.

I'm confused, are you saying you would prefer to disable the submit button without adding the `disabled` attribute?


Hmm, good point. I was trying to simplify a real-world example to shorten it, but ended up with something nonsensical. I was starting to share a better example but then realized I have other things I ought to do right now… I have it saved, will come back if I have more time later.


They're probably talking about the :disabled pseudo-selector.


I cannot see how react encourages you to forget :hover in CSS.


You can argue it is not React's fault as usual, but this is how:

    <Button styles={[...]}
      onPress={() => setState('active')} />

    const styles = StyleSheet.create({
        active: { backgroundColor: 'purple' }
    })
progressing into:

    <Button styles={[...]}
      onPress={() => setState('active')}
      onHover={() => setState('hover')} />

    const styles = StyleSheet.create({
        active: { backgroundColor: 'purple' }
        hover: { backgroundColor: 'blue' }
    })
which is frictionless and 'clean', vs the alternative:

    const styles = StyleSheet.create({
      button: {
        '&:hover': { backgroundColor: 'blue' }
      }
    })
The states here doesn't make a lot of sense, but I think it illustrates the idioms involved and how you end up focused on JS. This is so common I bet most devs who entered the market using React will see absolutely no issue in that.

Another familiar case is not having direct control over a parent/child component's styles, only their state, making it impossible to do it with CSS. Not exposing `style` is very common in component libraries, and using CSS-in-JS means you can't easily target them with CSS selectors.


The thing is, React works with plain CSS too. I think a lot of newcomers see that JSX looks like React-flavored HTML and naturally assume that React will have its own flavor of CSS, but it doesn’t. There are libraries for CSS-in-JS of course, third party ones. But implementing :hover on a button in React can be done just the same as in plain old HTML - with a bit of CSS in a .css file. Or for active/inactive states, you can use template strings to set a dynamic className, and target that className in the .css file.


That doesn't change the reality that there are a million projects out there using css-in-js - about 50% according to developer surveys. I doubt they are all written by newcomers.

The fact that React is a plain view library and doesn't provide / interfere with the rest of your tooling is precisely the problem in my opinion, and does not exempt it from the resulting mess. The lack of standards has led to the proliferation of a thousand supporting libraries, all special in their own way, in turn making the definition of 'good practices' nearly impossible; it also gives way to flavour-of-the-month development practices, where popularity (and not necessarily quality/fit) determines what libraries most people use. Similar discussions can be had around SSR, accessibility, bundling, compiling, code splitting, animations, component APIs, state management, persistence, fetching data, and so on.


Even if you do use a CSS-in-JS solution, which i'm not a fan of but have used in a few projects, they still don't do "hover in js"!

All they are are just fancy wrappers around putting more or less the same straight up CSS into the page. So you still use :hover or :active or whatever to do the appearance states in straight up css.

I still fail to see how React encourages adding event handlers to add hover appearances. A developer who thinks that's what to do with React was going to fuck that up with any technology.


Why on earth would you specify your styling in your JS ?


Being able to dynamically supply theming is a big one. For example:

    const Header = styled('header')`
       font-family: ${({ theme }) => theme.fonts.header};
       color: ${({ theme }) => theme.colors.primary};
       backgroundColor: ${({ theme }) => theme.colors.primaryBackground};
    `
...where the theme object can be swapped out on the fly for different sections of a web app.


Thanks i haven't thought about that one. But i'd definitely try to stick up to css even if I'd have to dev that feature. Maybe by trying to change the var(--color) i've setup in my colors.css ;

I'd note out that this feature is also so edgy that the pushed argument shouldn't even occur that much.

It would be like "Oh hei, I need to dig up that super edgy star shaped hole using that shovel so I'm using it that way. Oh see, when I use it that way i'm inclined to do X. Therefore my shovel is encouraging me to do X."


It's only an edge case until you handle white-labeling services for other companies, at which point you immediately need a way to conveniently tweak the styling of everything in your web app per white-label client, preferably in a database-supplied way so that your support team doesn't have to handle every adjustment said client wants to make.


Low quality developers will end up with low quality code.

I understand that frameworks might push you in a certain direction, but not this for React.


It's not React, it's new developers hungry to do everything in React, even as people better-versed in web technologies tell them to use CSS, etc.


If a mouseover should trigger an an action that causes the component to redraw, those the hover state persist?


Yeah, those are choices. You still have to understand what you're actually doing in the browser and the DOM, same as jquery or vanilla js.

I can see how easy it would be to accidentally get a bonkers js bundle if you start using external dependencies without monitoring how much code they ship.

Does React have a perf cost? Absolutely. But there's something else going on here.


The thing is that your tools should be making good this easier than bad things. And React is the opposite of that.


So youb are saying that React makes it easier to do bad things than good things? Would you mind extending on that?


I tried to do that in the article. But the thing is that when you're writing a little piece of code React makes it dead easy to make hover in your code. There a lot of little things like that.

You start out with good intentions, but after 4 years of different people certain parts of codebase start looking really weird even with code review. You just can't catch every little detail.

And this is why tools should make it easier to do good thing (good as in what you'd want to see in the end).


> after 4 years of different people certain parts of codebase start looking really weird

That's pretty much how software development works though. It's what happens when people with varying skill levels, schools of thought, preferences and approaches to problem solving all work in the same code base. Heck, it'll probably happen even if it's your own pet project: four years is a long time in something as fast-moving as web development, especially when you factor in things like market-driven priorities (E.G. adding new features vs. fixing old "good enough" crap kicking about).

I honestly doubt there's a single software project in history that doesn't have shady corners after a couple of years, no matter how excellent their frameworks, conventions and developers are and how draconian their review process is.


That is true, but the effect various "quirks" have on result are very very different.

I did not decide to switch from React with a light heart, it was a long and painful decision. But it seems to me that React's path of least resistance leads to a slow bundle, and you're going to fight an uphill battle.


they were using handrolled SSR in clojurescript, I'm not sure they set up code splitting.


> I'm not sure they set up code splitting

Don't think that matters. I've written (from scratch and inherited) and deployed many ClojureScript frontends, from one page ones with lots of interactivity to 30+ pages/sections, none of them reaching the size of 2.5MB minified (when using the production settings for Closure Compiler). Add in SSR and/or code splitting and the weight should be nowhere near there, leading to the guess that something is wrong in their config or they are embedding binary files into the JS asset.


Oh yeah, binary assets. Yeah, it is certainly binary assets. We just embed node.js in there. /s


When people are discussing one's article (I assume it's yours based on your profile) I know it's easy to go into defense. People may criticize, and that hurts. But before lashing out as you do in some comments here, consider that the readers don't have the same background and context as you, and we all discuss based on those. Throwing out React is a huge step for most projects, it may have been good for you, is probably not right for many, and is certainly a discussion worth having.


You know, executables are not the only type of binaries, embedding a PNG file into the JS asset would also be considered embedding a binary asset. But I'm sure you knew this, you're just trying to be funny.


:-) yeah, in the heat of the moment. There is of course things we could do to improve bundle size, but it's all components and logic. We didn't do any css-in-js or any other fancy stuff.


> consider that the readers don't have the same background and context as you, and we all discuss based on those.

Okay, so how about not inventing outrageous ideas on the spot and just consider what is written?

It's equally as hard to unload what's been boiling in your brain for last half a year. I'm reading comments and trying to decide where to expand my post, of course, but this "binary" thing just got me laughing.


Actually webpack has the url-loader which allows embedding binary data as base64 data URLs in your CSS bundle. If used judiciously it can even be a good thing too.

EDIT: I mentioned webpack since the thread is about react, but you can do the same thing manually by putting the base64-encoded data in a data URL on a CSS file.


We were recently looking at https://ant.design/ which is one of the most popular React component frameworks. The components are nice and the design is appealing, but the 1.2 MB size is off-putting (https://bundlephobia.com/result?p=antd@4.5.3).

"Wait," we told ourselves. "Surely, we can do some tree-shaking, replace momentjs, be strategic in our imports and shave a ton of that off"

With all of that we got it down to 1/2 of the size, but that was still too big for our needs.


> dead-tree elimination

The practice of removing unneeded code from a bundle using static analysis is commonly known as “tree-shaking”, but I like your version better. :D


Looks like "dead code elimination" is what this is generally called in compiler-land - another thing rediscovered in javascript-land and given its own name.


> This is some strange reasoning that I have yet to seen myself. If you can do the hovers states/drop down menus in CSS, why not do them in CSS, even if you're using React?

Because when requirements change and your hover animation needs some additional flare to it in certain cases, you'll be glad that you had automated unit tests to verify original behavior still works along with your customizations.


> This is some strange reasoning that I have yet to seen myself. If you can do the hovers states/drop down menus in CSS, why not do them in CSS, even if you're using React? Seems to be blaming something on a library that the library has no care about in the first place (which to be frank, seems relatively common in web dev circles).

Let's all be honest, that's complete laziness or lack of knowledge by the developer.

> And holy guacamoly, how do you end up with this?! Seems that something was surely wrong in the compilation options, forgetting to mangle names or something, missing dead-tree elimination maybe?

Maybe sourcemapping?


I have been with React for 4 years now. Link state can be driven through React if you are testing some state or URL (active URL). But hover should never be React driven. I can not fathom why anyone would do that.


The first legit reason I can think of is if you have a particularly complex UI with fixed/absolute/sticky positioning and you need tooltips. In this case, CSS gets in the way and you need to render the tooltip through a tooltip and so need to set the position using JS on hover.


Like in https://ant.design/components/tooltip/? However, hover used.


The framework and libraries and information you find on the internet definitely encourages you to go for animations via JS. Might be a holdover from before CSS3, but I'm not sure I've ever seen people really try to use CSS3 for their animations unless people were working on their own toys. Just google "animated X react" and 80-90% of the articles are massive JS components with a little styling in the CSS.

For size of the bundle, just remember the node_modules black hole meme. The amount and size of JS libraries is no joke, it's wildly out of control. 2.5MB minified non-gzipped is common mostly because of a paradigm of "once you gzip it it will be small, and inflating that doesn't cost anything, and this way everything is preloaded!". Libraries come with a bunch of images embedded as Base64 encoded strings, the full localization tables, 40 1kb depedencies (left pad and friends), etc. etc. etc. These are all the default and no one changes defaults.

To make a reasonable web application today without all the insanity, you have to be very disciplined, because everything is pointing you toward doing dumb or crazy things.


When I first read the haiku at the bottom of the HTMX homepage, I had a flash of insight.

javascript fatigue:

longing for a hypertext

already in hand

What are we doing? State management libraries? Hypermedia is the engine of application state. Send data to client as HTML and have the client apply styles over it, rather than converting from JSON to local objects and storing in some sort of reactive data store. You will find that semantically structured HTML is almost as economical as JSON.


The Zen of jQuery moment for me was realizing that many of our bugs came from trying to separate the source of authority from the system of record.

It was a tremendous amount of busy work keeping metadata about DOM elements stored separately from the DOM elements, and hanging the values off the element was so much simpler in initial implementation, maintenance, and exploring other people's code.

Everyone was so excited about the virtual DOM in React 'n friends, whereas I saw both the functionality and the excitement as warning signs that I should stay away. I hoped this would turn out to be a fad, but it's a little long in the tooth for that scenario to play out now. For a while I watched news articles for the chinks in its armor, these days I'm more focused on other tools that satisfy niches, and wondering what will end up being the 'CSS3/ES6' moment for React where the browser does 20% of the work to have 80% feature parity with React.


I think you may be interested in Web Components.

https://developer.mozilla.org/en-US/docs/Web/Web_Components


Why does is handling state on the backend better than handling it on the frontend? You will always have the cost of waiting for the server to return the full html every time you want something to happend and still you will have cases where you are handling state on the frontend


> return the full html every time

The post mentions their HTML size decreased - most likely due to reduction in intermediate components and nesting.

Note that with an SPA you need to send templates (in the js bundle) + data (json api) to the client, which by definition will be at least as large as static HTML for the same content: HTML is nothing more than the template and content already baked in. In practice the JS templates are much larger due to containing the entire application logic + compiler overhead.

Finally, the benefit of having the templates already loaded is only realized over longer timespans, when probably half of your users are coming in with an empty cache anyway.


> which by definition will be at least as large as static HTML for the same content

Not really by definition - tables rendered on the server side are a good example of a payload that can easily be larger than template + dataset.


Transfer compression will usually take that down to the point where it's negligible. By the time you're talking enough data for this to matter, things like pagination will likely be far more important.


> The post mentions their HTML size decreased - most likely due to reduction in intermediate components and nesting.

Mostly because of initial data. Index page is still on react, so you can go on https://kasta.ua/ and look for element `#initial`.


Indeed. Still a clear win!


I have not seen a good case of this. Can you point out one? Managing state on the server is trivial; sessions and database and that is it. How does handling on the client work? Well it works if you double up on the server; it is a crime when there is some SPA that manages state client side; something goes wrong and it is stuck, press reload, state gone; login page. It is annoying to me as dev, it is creating serious anger for people who do not understand this. They press f5 and are logged out. Wtf. Or their shopping cart is now empty. So if you want to fix that you have to handle state on the client and then sync with the server; how is that better than just having it on the server? I am a backend dev but I see more and more of my frontend colleagues move stuff to the server: it is just easier to reason about imho.

Sending over generated pieces of html that update divs I find a quite nice compromise: not the complexity while having updates without refresh. Intercooler sounds like that.


I’m with you, but I do like applications that can continue to operate seamlessly without a connection or with an intermittent connection. Although for these I would also expect them to persist state across refreshes with IndexDB or similar. Basically, I want my applications to work similarly to a git workflow, with merges and conflict resolution with the server only required when you hit “Save” (but maybe preemptively noticed, depending on workflow).


Maybe implement authentification properly and store a refresh token that you can use to trigger auth on app load.


It is absolutely fixable, but the extra complexity invites people not to fix it while on the server it works as default.

I have integrated some very popular backends the past months and their portals are all SPAs and they all have this issue; hit refresh and you lost where you are, thrown back to login.


There is a great article that went uncommented on HN about Architectural Decision Records.

https://github.com/joelparkerhenderson/architecture_decision...

The simplest of them include "consequences" of architectural decision. For PWA these include the development of that kind of features.

Now to be absolutely honest. I prefer having a dedicated back-end that does one thing well and a front end that does one thing well. Even if I have to put in extra effort with few features just to have my source-code more dedicated and therefore cleaner to work with.

The thing is that PWA not only included the front-end dev, they also reduced the complexity of back-end. And that's a huge win I'd pay with these kind of features happily.

Stop crying around on HN and go dev your feature


> Stop crying around on HN and go dev your feature

Not reddit here, people like to talk about and ponder eachother's opinions.


Honestly I like HN but the constant talk about PWA vs pure HTML apps has became tiresome and unproductive. I haven't seen a single argument that's properly stated about why we shouldn't do them.

Having argument alike "Oh I have to dev that feature therefor X sucks" is not a good enough opinion that i would like to read here neither.


People implement different ways of doing things all the time; they have to get ideas somewhere and they also have to see their work is worthwhile for others. You might or not like the current state; you just suck it up, I get that, we all do. We also can complain and try to do something about the things we do not like. Every new software is created for some reason; money opportunity, interest, scratching an itch or trying to get rid of pet peeves. Nothing wrong with that and personally I like to read about people’s issues with software; it keeps me confident that ‘this’ is not ‘it’, because if it was, I would become a full-time pizza chef. In the meanwhile, obviously I suck it up and build things; I can still hope for better. For me personally, Phoenix and Blazor(server and maybe client) show that I am not the only one annoyed by the stuff ‘everyone uses’.

Noone (well...) sits behind HN crying while doing nothing; but it is a platform where people go who actually make the changes or at least talk about them without dismissing them with ‘just use node/react and stfu’ or, almost the opposite, ‘just use wordpress and stfu’ like much of reddit and other programming forums (as far as there are).


Theoretically, you have a point. Practically, you are reading this comment on a website that not only does exactly this, but refreshes the entire page (rather than a small block of HTML, intercooler-style). The performance benefits are evident.


This site also has almost no state at all per-user.


Every time you collapse a thread, that's saved on the server. Then, if you return to the page, it will render those threads collapsed. So that's a decent amount of state.


I disagree. It’s Boolean and flat (there is neither sequential nor nested logic around the state). It does recurse for each sub thread, but only in the case where there is no state. There’s also no business logic built around it, only display logic. It also is updated from exactly one location in the application.

That said, it’s a very simple and effective design. It’s an excellent program. But it’s not a fair comparison against other applications which, for one reason or another, need significantly more advanced state.


Any time you vote a comment, that's state as well.


> The performance benefits are evident.

I mean, this website only loads a very small amount of text and has barely any user state besides a list of comments and posts. I would hope that it loads quickly.


"The performance benefits are evident" is a bit too authoritative to me. Would you mind giving a summary of the performance benefits?


Because the user can't be trusted - on several levels.


Treating 10KB of JavaScript as equal to 10KB of HTML is a big mistake. They transition over a network in the same amount of time, but HTML is far faster to process. Websites with large HTML sizes often display far faster than the same information that use JavaScript client-side frameworks. Often the framework is still trying to figure out which way is up, while the HTML version is already being displayed. It's a trade-off that isn't discussed enough.


I wrote a game engine with a DSL that renders as detail/summary html blocks. It wasn't hard to end up with a page that was hundreds (or thousands) of megabytes. Any given browser really had no issue until it was over several hundred megabytes.


Agree, this is so true. I will give it a few years before we are all back on server rendered web apps.


Server-rendered web apps never went away. They are still extremely popular. The tech stacks have only diversified.


you are SO right... but web developers have decided to go for complexity over simplicity and elegance.

IMHO this happened just because complexity and fads generate so much more money, in the long run.


It would help if there was some objective way of measuring "elegance" in software.

Some people think hammer factory factories are elegant.... ;-)


We need to look at it from two sides: if it’s good for developers and if it’s good for users. React was great at former and terrible at later.

React is not "bad for users". Developers build complex, fast apps with React all the time. It can be fast, but if you make mistakes with it then it's easy to make something very, very slow.

The app I work on is huge. It's ~8MB of uncompressed React + Redux in development (much smaller in production though), and pages include a lot of assets so they can weigh in at 23MB in the worst cases. A complex page can have up to 60,000 DOM nodes under React's control with thousands of event listeners. It starts in about 3s and never drops below 60fps.


> A complex page can have up to 60,000 DOM nodes under React's control with thousands of event listeners. It starts in about 3s and never drops below 60fps

Can one really make such affirmations regarding client rendered web apps? I'm assuming these numbers aren't solely measured on localhost in some state of the art development machine/device so won't it depend on the client's machine specs, browser, usage, bandwidth, etc?


I don't know what bandwidth has to do with react managing the DOM.


A naively built app will do things like take some user input from a form, send it off to an API and await the response, and then update the UI when if the request is successful or display an error if there's a problem. That means the user has to wait for the request to complete before moving on to their next task. In other words, the DOM update waits for the network. If you have a slow connection that feels horrible (snarky frontend dev note - if you build a server-side rendered app it's how everything in the app works. Sucks if you have a slow connection.)

It's better for the user if the UI assumes the request has been successful and updates the UI with a temporary success state, and then undoes the update if the request fails and gives the user the option to recover their update and try again. Most of the time there won't be a problem (especially with good client side validation) so they'll never see the recover state, and they'll never need to wait for a network request to finish either. Obviously you shouldn't use that sort of UX pattern for critical things though.


Ugh, this has got to be one of my biggest bugbears with SPAs. This pattern fails far more than you obviously think it does, and when it does fail, it's usually handled so poorly it's worse than the 'cure' you're peddling.

Give me a clean, server-side form submission any day over the "has it worked, hasn't it?" inconsistency of SPAs.


This is how i manage more than 60,000 DOM nodes https://github.com/developit/preact-virtual-list ; actually i think i could handle 600,000 and still have no problem.


You just removed an usefull features from the user.

The virtual list make ctrl+f not working anymore, and I hate every single react page using it. Suddendly, on this webpage using virtual list, you ctrl+f something, dont find it, because the element does not exist.

A vue or angular manage your 60k elements list without any issue, you dont need to virtualize your list, and the search feature your your browser keep working.

And if your don't use a virtual list, react get performance issues starts when you get only a few hundreds of elements and you want to filter them.

Also, it start 'instantly' with other frameworks, not in 3 seconds, but below 500ms.


There is a find feature in the specs already. My users actually need to be able to interact with a lot of rows.

I can just bind ctrl-f to my search feature.

Also, it doesn't take 3 seconds to load but few ms as well.

See, you don't just pick a tech, you need to start from the specs and get what the user need.

Also, in pure HTML (because i tried it) ; the same rendering would be hanging anytime you try to act on something.


Now it doesnt work:

- On mobile, using menu to search in browser menus.

- When users use F3 to search, an alternative to ctrl+f.

You added a few bytes to reimplement a browser feature, and spend time on a feature that already exist, because of your tech choice. There is still probably some accessibility issues.

You can have easily 60k rows in HTML without any performance issue, you just need to pay attention...


What are you talking about ? Are you saying a search feature is not necessary in my specs because you like using f3 or ctrl-f ?

"On mobile, using menu to search in browser menus" -> This is not a sentence.

I do add a "few bytes" to "reimplement" a "browser feature" ; but the reality is that i HAVE to implement search because it is hitting on 730000 per year records;

How is you ctr-f or f-1 feature is gonna search on 2190000 rows in 3 years ?

Again, you are too happy about your opinion that you spite it out like truth without even understanding specs.

Specs is what allow you to understand what's required, it makes no sense what so ever to talk about technology without understanding WHY you use it in the first place.

Is it meant for public ? employees ? How many rows do they deal with in their actual everyday workflow ?

You are pushing your workflow so much that you don't even consider about your users and that's another red flag for me.

PWAs have given so much in term of capabilities and mastery that I honestly wonder why I'm still talking about such things on HN while proper workflow about architectural design are ignored here.


""On mobile, using menu to search in browser menus" -> This is not a sentence. " -> Sorry, I meant using the browser search feature by invoking it from the contextual menu of your browser, which is the only way to spawn the search toolbar on mobile.

" but the reality is that i HAVE to implement search because it is hitting on 730000 per year records;" -> True, there should be a search feature with a backend API.

"Again, you are too happy about your opinion that you spite it out like truth without even understanding specs."

Yes I want too far, your use case does require extra logic, because it's not good to send that much data to the user. I'm not pushing a workflow, im not a front end dev, but i do maintain frontend apps sometimes, i'm simply angry at React, because it cause pain to me as an user.

It's noticably slower (https://medium.com/dailyjs/a-realworld-comparison-of-front-e...).

The virtual list may be required for your use case, but as a user, I often met virtual list:

- Used for less than 100 elements

- ctrl f is not hooked on the 'webapp search feature'

- the 'webapp search feature' is noticably slower than the browser.

Also, here some reading, from a framework I don't use, about the virtual dom:

https://svelte.dev/blog/virtual-dom-is-pure-overhead

Now, not as an user, but as a software engineer, my soul cry when I see the architecture principle of such a framework fixed with a workaround.


Thanks for the read ! I didn't know svelt and it seems like it has solid motivations. I should definitely check it out.

I'm actually more of a preact fan and the community Jason Miller has created, their work have inspired me a lot in concern of front-end and javascript dev.

ctrl-f hook is effectively something that has to be considered when implementing such feature.

I'd argue that the list & search feature it self is hard, not as something to put on and program and get working, but as a user-experience and design perspective (I'd like to thank you about pointing that out by the way, I will definitely spend some time making sure it's there for my app)

The reason I can think of, is that we do actually take control over parts of the applications in a way that was not imagined when browsers implemented search function.

It almost feels like the "With big power comes big responsibility", but i don't like the word "power" in our case, so I'd change it to something alike to "With features that have extra consequences we need to have an equal amount of extra care to even considering them." as if it was hidden and we needed to understand the tool better to have it properly done.

I guess front-end dev is easy to get in and understand, but hard to master, and that could be why we end up with these rampant "half-done" apps that do not take those extra steps.

In the end, the rushed dev will probably just look to have it's app working and will not check these kind of "bugs" that have been caused by his decisions,

Tbh, without this discussion i'd probably have missed it too


This is admittedly nit-picky, but you're not managing 60k DOM nodes if you're using a virtual list, that's the whole point of virtualization. You might have 60k items in your list but you're only ever rendering a tiny subset of those items based on what is visible (with some overlap).


It's not virtualized. It's not even a list. It's a very large SVG with lots of foreignObject bits in it. The app is a diagramming tool - imagine Visio but for lawyers to map out contracts and ownerships between hundreds of corporations.


Sorry, someone else mentioned a virtual list component and I assumed it was you without looking at the username.

That's pretty impressive, I've run into perf issues with far fewer actual DOM nodes than that!


You might get better performance with the <canvas> element instead of SVG. I've noticed React can struggle with SVGs containing complex subtrees.


Can you share a link to a react app that is both complex and fast?


> we’ve discovered that React also leads to some questionable practices. Like hovers in JS (rather than in CSS), drop-down menus in JS, not rendering hidden (under a hover) text (Google won’t be happy), weird complex logic (since it’s possible!), etc. You can have a React app without those problems, but apparently, you have to have better self-control than we had (nobody’s perfect!).

idk if this is fair. OP was using some pretty niche tools (clojure) whereas best practice React metaframeworks like Nextjs may have addressed some of those pagespeed issues.

additionally, I highly doubt that the author has replicated this functionality by using his new turbolinky framework.

So the post is better titled "I improved webpage speed by throwing away React AND a bunch of UI requirements". which is fair dinkum, but less exciting.


> additionally, I highly doubt that the author has replicated this functionality by using his new turbolinky framework.

Which functionality?

> a bunch of UI requirements

So your point is that you don't really know, but let's blame them for trying, or what?


It's hard but take a few minutes off before responding to snarky comments.

Please answer their indirect question: "Did you replicate every of the react app's functionality in the app?"


This is why it took four months, we had to change all React-isms to something that works in both modes. One particular thing could load less HTML, and it'll require some attention to make it so - but as we're deprecating React version of catalogue, it'll be easier to do.


> So your point is that you don't really know, but let's blame them for trying, or what?

I think GP's post is that _you_ don't really know the landscape which was why you reinvented the wheel, which I tend to agree with. You even agree with this unless you're being rhetorical here, no?

> Which functionality?

Let's take a look at every feature we see on the next.js page (https://nextjs.org/):

Zero Config Automatic compilation and bundling. Optimized for production from the start.

Hybrid: SSG and SSR Pre-render pages at build time (SSG) or request time (SSR) in a single project.

Incremental Static Generation Add and update statically pre-rendered pages incrementally after build time.

TypeScript Support Automatic TypeScript configuration and compilation.

Fast Refresh Fast, reliable live-editing experience, as proven at Facebook scale.

File-system Routing Every component in the pages directory becomes a route.

API Routes Optionally create API endpoints to provide backend functionality.

Built-in CSS Support Create component-level styles with CSS modules. Built-in Sass support.

Code-splitting and Bundling Optimized bundle splitting algorithm created by the Google Chrome team.

Now, do you want some or any of these things? How long do you think it will take to incorporate them into your new tool which you just rolled from scratch?

I guess at this point I'll add in some opinionated views here. At a certain point along your software engineer journey towards becoming a senior engineer, you are supposed to understand that undifferentiated heavy lifting and implementation is a bad strategic move in terms of technical strategy. If I am your CTO and you are telling me that you want to write your own Intercooler-esque library and move all of our core frontend checkout code away from React (an ecosystem with conventions that much of the rest of the world uses) and towards a proprietary solution I am going to ask you for a good reason. That is to say, I am unlikely to be persuaded by "I didn't spend enough time deeply researching how the rest of the ecosystem's users handle these issues" and I am likely to gently remind you that we will likely get more mileage out of investments that make our user experience better rather than scratching the itch to write a new framework.


That is a good list. Have you heard about ClojureScript? Obviously, TypeScript is not supported, but everything else is.

> How long do you think it will take to incorporate them into your new tool which you just rolled from scratch?

Here is the news: we will never add them to the new tool. No TypeScript, no compilation, no routes, nothing.

> If I am your CTO

Good news you're not, right?


> Here is the news: we will never add them to the new tool. No TypeScript, no compilation, no routes, nothing.

I think that's part of the issue. You have built something outside of an ecosystem. Have you planned for what will happen after you leave? Have you been on the receiving end of inheriting a codebase written in a language (like ClojureScript) that was buzzy for a while but then never took off? It happened to a lot of people with Coffeescript.

> Good news you're not, right?

Are you sure that's good news? Because what I see here is a poor choice of a niche language (ClojureScript) which lacks traction and will probably die long term. Moreover, because it lacks an ecosystem (or more accurately, that ecosystem is not the exact flavor you want), you've taken liberties to write things completely from scratch. Why not use Next.JS? Why not use Rails + Turbolinks? Why not reconsider writing crucial infrastructure from scratch when it already exists in many forms? Most damningly, why should you be trusted to reinvent the wheel if you lack the patience or thoroughness to go through previous solutions to this problem and put together a convincing argument for why they fall short and you need to make something new?

These technical decisions around infrastructure, stack and ecosystem are ticking time-bombs. They are almost certainly a long term technical risk and an eventual roadblock to recruitment/onboarding. I think it's unfortunate that you have wasted your time reinventing problems that have already been solved instead of focusing your talents onto areas which deliver more value to your customer. As you are working on e-commerce, this could be anything from marketing and demand acquisition to checkout funnel conversion to fulfillment, up-selling and cross-selling.

If your organization's leadership incentivized engineers to seek these kinds of investments, it would probably result in you choosing a stable stack (which, while imperfect, is good enough, has a larger ecosystem of maintainers than just the company, and so has less of a risk of blowing up in a few years) so that you could focus on investing into more customer-centric engineering. It's a shame because I think that this kind of engineering is the kind that helps engineers grow and mature in seniority. It gives them the capability to become technical and people leaders, who are exactly the kind that most companies that employ leaders need to have.

But that can never happen when engineers don't develop the judgment to determine which problems are strategically valuable to the business and which ones amount merely to overhead. And of course, many companies see no need to invest in or to empower their engineers this way, so this kind of thing keeps on happening because you need to work on something stimulating or what would be the point of work? Feel free to disagree, but based on what I've seen, I think you are working on the latter. I guess all I could ask you is imagine what kind of amazing results you and the company would see if you focused all that incredible energy you put into this infrastructure into delivering customer value!


As others have commented, this doesn't seem like it's React's fault.

But it does illustrate how React isn't a magical solution to the front-end woes and how complicated the whole thing still is to do right.

I'd still use React for projects, but for now I've been incredibly happy with the LiveView solution that Phoenix/Elixir offers (or the variants for other frameworks. Blazor for C#, LightWire for Laravel/PHP?).

It's surprising how often I'll work on something and realize that the solution is quite simple now, where before it would definitely mean some serious thinking.

For example: libraries. I remember so many projects where I needed do do some date formatting or manipulation. Moment.js was the obvious solution, but including the whole library was not an option.

With LiveView I can just pull in whatever dependency I want, because it's all server-side. It's only the markup diff for the specific component that gets sent down the wire.

Or security. I need to show a user, but only a 'friend' can see the email address (or other profile details). The 'old-fashioned' way would involve separate API calls and a certain nervousness that perhaps I might end up in a situation where the front-end behaves how I expect, but the API calls somehow expose non-friend data.

With LiveView I can just add a conditional statement to the view, and since the resulting HTML is all that does to the client, I'm done!

Of course this only works with a persistent and relatively low-latency connection, but I can't remember the last time I worked on a project where this wasn't an implicit assumption.

I'm perfectly happy using React/Next/Vue when necessary, but it's a really strange experience to read these kinds of articles and threads these days when for so much of my day to day these problems just went away.


LiveView is also very interesting. The main reason we went other way is that amount of connections and state held on server will be really high, plus we have some clients with bad connections and regular HTTP works much better on those than websockets.


LiveView can use HTTP longpolling.


How on earth does React encourage the bad practice of “hovers in JS”? React absolutely has zero influence on this - that one is entirely on the developers.

I’m also exceptionally confused why the Pagespeed score was sitting at just 5/100 - it doesn’t sound like the dev team actually understood the result and attempted to resolve it.

It sounds like the author has found some new technology they’re happy with for now, but it sounds like all the previous problem were self inflicted and they’re bound to repeat them again.

While different tech and frameworks has an influence in the problems you’ll face, you’ve still got to do good programming.


If you are writing JS styles, you don't have access to pseudoselectors like :hover. A quick solution adopted by many is simulating hover with onMouseEnter and onMouseLeave event handlers.

This can be remedied by using CSS or styled-components.


This is nowhere near a common practice (I've never seen it on any work project, or on community projects). React does not promote or advocate or make it easier to do it this way, rather than the proper way in CSS.


It is actually in the react documentation. https://reactjs.org/docs/dom-elements.html#style

I'm not saying that's how it should be done. I'm just clarifying what the author of the article was referring to.


Correct, it is documented. That's what documentation is - to list out and describe the API surface.

The entire documentation block for that is prefixed with a big yellow-box warning:

> using the style attribute as the primary means of styling elements is generally not recommended.

And then it links to https://reactjs.org/docs/faq-styling.html which just says over and over again "use class names"


So just to be clear A) it didn't always have that warning and B) I wasn't defending the practice. Did you read the article?


Reading all of the cynicism on the comments I wonder if we are taking into account the business context for the development of the app. I’m not a fan of react even though I use it daily, but the licentious nature of react allows me to build/modify features pretty quickly, albeit, low quality. I’m as idealist as the next person about a good architecture, but if the code you are writing is for a new product that is still trying to find a market-fit spending a lot of time in the architecture of something that could drastically change doesn’t make much sense to me.

All that I’m saying is that, as opposed to some other opinionated frameworks, react let’s you do pretty much whatever you want, which is great for speed development but can also exhausting in the huge amount of decisions that need to be made.


If you’re going for speed of development, it’s hard to beat server side rendering in Rails. The creator built Rails for speed of development and happy engineers. It’s also very opinionated (convention over configuration) which helps me just build out features rapidly without making thousands of pointless decisions.

I am not opposed to React for like, a calendar application, or a particularly complex part of a web app, but most web apps have 90% CRUD and 10% shiny.

And with well-tuned SQL and turbolinks dropped on, Rails pages load in 30-60ms with no page refresh.


The happy engineer concept is a much neglected one imo.

Rails devs who’ve mastered a lightweight js framework like Stimulus and perhaps a webhook utility like ActionCable are immensely more productive than a team of very separate front and back end devs.

Basecamp take this a level further whereby each dev is quite heavily involved in the design process, and their designers can also code in Rails.

With Hey I think they’ve shown that it can not only be lightning fast but pretty shiny!


Oh I agree, I lost a lot of nuance with my trite 1-sentence summary. Shiny is mostly good judgement, good taste, careful organization and a lot of sweat resulting in well-crafted CSS.

Especially in the past few years, as browsers have implemented a lot of animations and scaling in CSS which offloads to the GPU. You don’t want JavaScript/CPU doing that math anymore.

Component-based JavaScript frameworks “force” us to organize our CSS in the same way outlawing cars in favor of bikes bikes ensures everyone follows the speed limit.


I agree with you, even thought I might personally prefer Elixir/Phoenix :-)

In my particular case thought I've been opting for Golang given that my web app needs to perform a little more that CRUD. And in regards to the CRUD part I've been heavily relying on PostgreSQL functions, thus instead the pain of a ORM I just have simple raw sql `SELECT * FROM api.get_user_data(?,?);`

I'm not saying that is not possible to to things like this on Ruby, but when your application is a little more than CRUD RoR starts getting on my way...


Sounds like you and I have similar approaches, and I just have more CRUD to manage in my case. For e.g. payment processing endpoints, I use Golang. Managing 3rd party SDKs is nicer in Golang, imo.

I also heavily rely on Postgres views, which ActiveRecord just sees as read-only tables, so my Rails code can still call `user.api_data_entries` without any ORM wrangling.


nice!


> react let’s you do pretty much whatever you want

Ok, so I haven't gotten around to learning React yet, but I understand it to be the next iteration of what Angular, which I have learned, was trying to be. Angular "let you do whatever you wanted", too, which made me wonder why it was even there in the first place. Javascript lets me do whatever I want, I just have to write a lot of code to do that. In theory, a framework ought to reduce the amount of code I had to write, but I found that I was actually writing more code to work around the 90% of things Angular didn't do than I would if I just used Javascript and DOM manipulation. Frameworks that "let you do whatever you want" usually end up being the thing they're supposed to replace with extra steps.


React is very very little, is not really a framework, l and honestly I think most folks are really either talking about JSX when they talk about React or they are talking Create-React-App and it’s ecosystem (which really is much more like a framework).

JSX comes down to being a convenient way to write HTML in your JS instead of JS in your HTML. After that, it’s just vanilla js.


> but if the code you are writing is for a new product that is still trying to find a market-fit spending a lot of time in the architecture of something that could drastically change doesn’t make much sense to me.

"Nothing is more permanent than a temporary solution"


It's true.

But, after having witnessed, and having committed, countless acts of perfecting something until it died on the vine, I would consider, "This hack is hard to change because so very many people are paying us money in return for the privilege of relying on it," to be a very nice problem to have.


the pendulum can swing too far in either direction. but pumping out features with no regard for how they will be maintained is a recipe for lower velocity and less stability in the future.


> pumping out features with no regard for how they will be maintained

That doesn't seem to be the position that's being advanced here. TFA, for example, starts out talking all about the author fell in love with React for its easy maintainability, before moving on to the need to make some tweaks for the sake of user experience, and finally ending by proposing a design that is based on a fairly principled set of design tradeoffs.

And the parent commenter, at least to my reading, isn't saying, "phooey, who cares about maintainability," they're saying that, while they don't like React, they do have to acknowledge that there is pragmatic value in it not being so rigid that you can't just grind out the code when business realities mean that that's what you need to do.

Long story short, if what you're trying to advocate is a flexible, middle-path-type approach, I don't know that this is the best place to go searching for a debate on the subject.


true, but for me writing highly architected features that take 3x or more time to write just to steer away because of business needs (i.e. doesn't sell) seems to me more painful.


The exact thing that makes development fast is not to make decisions. In fact, this is such a fundamental principle that Uncle Bob spells it out this way: "A good architect maximizes the number of decisions not made" and I wholeheartedly agree with that. Also as a sibling comment mentioned it, Rails and similar batteries included frameworks (like Angular for example) are the way to go for rapid prototyping, since they include everything you might ever need.


Not opposed to server rendering or backend heavy sites, but a lot of people are able to get much better Lighthouse scores for fairly complex sites through a combination of webpack code splitting, service-worker based caching, and CDNs.

So, the problems here may have had more to do with the clojurescript stack (which I am not much familiar with), or author's lack of familiarity with javascript optimization strategies than react, SPA model or client side rendering.


Code-splitting is supported in ClojureScript. Compiled CLJS is just JS, so service workers and CDNs should work in the same way.

However, CLJS really does feel like application development (which is why I like it, tbh) and the community doesn't seem to pay much attention to the use-case of regular web pages which just need some JS components to add interactivity. Googling for strategies to optimise CLJS for that use-case won't get you many results - you have to understand the stack well enough to do it yourself.


For sites that are content-heavy I've started wondering if it makes sense to have the content server-side rendered the old-fashioned way, and use multiple React roots for the parts of the page that need to be interactive. You can still use Redux etc. for managing app state globally (though not React context), and you get most of the gains of load time, and of using React where you need it.

It seems everyone uses <body><div id="app-root"></div></body> but React is perfectly happy being scattered around the page.

It's just a thought, I haven't tried it myself yet - has anyone else?


We do exactly this within SharePoint. We have various react parts that look things up in the database - sometimes quite heavy queries for reporting.

We have put together a small collection of fairly generic react apps that mean we can put one on a page, put in some config and have it displaying a report generated in SQL in about as long as it takes to write the SQL for the report. We have used this approach for generic SQL reports, SharePoint lists, imgage retrival and entry forms. Having the library of options to hand has meant we can confidently assemble a new (fairly complex) page much quicker than previously - and be confident that it will work as it is reusing known good components.


Depends on your business case / use case and where you want to draw lines. A lot rides on which parts you perceive as being "application" and which parts aren't.

For instance, you could build an e-commerce shop as an SPA, or you could target specific parts that process user interaction dynamically / fluidly - such as the checkout process, adding to a cart,... - and consider those as separate applications.

There are also trade offs. Search and navigation, for instance. You could build an SPA for an entire search engine. But then you may end up doing heavy lifting, like dynamically managing URL state through routing components, which is something browsers already do themselves: the only gain being that you don't reload the entire page.

So, the big question boils down to: what are you really trying to solve? A UI/UX problem? A performance problem? A maintenance problem? Something else? And who are the stakeholders, who's using the stuff you're gonna build? What are their intentions and motives?

That's when you come, to a conclusion: there's no silver bullet. The architectural design you choose needs to be an informed choice above anything else. And it should be informed by your specific context rather then the affordances provided by the tools at your disposal.

The hard part is sitting down and taking a bit of time up front to think and articulate an argument that, given your context, validates choosing a particular strategy. (Personally, I tend to sit back and stare at the ceiling with my notebook and a pencil, but that's just me.)


At my old company we did this approach for some of our pages. We were transitioning away from our old codebase page by page but rewriting the codebase took longer.

New features were still desired so we built/repurposed react/redux components into what we called “hybrid” pages. It worked great for us!


This map is actually a wordpress plugin that loads locations from google sheets into a JS var that a react bundles loads. Client already had the rest of the site built in Wordpress so easier to just make a plug-in with a shortcode that loads the react bundle into a div.

I and some other built while i was at Poetic in Houston, Tx. https://www.houstonfoodbank.org/find-help/agency-locator/


Yes, a hybrid. Routing and initial rendering handled by a server-side MVC framework, like Laravel. And on each page you can have one or more micro SPA-s with a lightweight JS framework like Mithril or Preact. You can even send the initial payload as JSON in a special JS global variable inside a script tag.


React Portals could also help here, allowing you to still use context and the like.


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

Search: