A tale of webpage speed, or throwing away React(solovyov.net)
solovyov.net
A tale of webpage speed, or throwing away React
https://solovyov.net/blog/2020/a-tale-of-webpage-speed-or-throwing-away-react/
300 comments
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
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 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.
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.
> 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?
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?
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.
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.
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.
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.
> 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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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?
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?
I've been doing Ajax practically since Day 1, and in the olden days when I was young and stupid I actually sent DIV IDs back to the server, which the server would then reply with -- in XML -- to tell the client which DIV to replace.
Then I realized Javascript could do lexical closures which meant the client could keep track of the targets, which also meant the server no longer had to care about things servers shouldn't care about.
The next realization was that DIV IDs are global variables and thus in most cases a bad idea, so now my event handlers automatically search upward (and sometimes sibling-wise) from 'this' for DIVs matching classes or other selectors, to keep the scope local.
This TwinSpark library seems like the next iteration of all the good ideas, plus even more flexibility and proper separation of concerns, without JQuery or other dependencies.
Bravo!
Then I realized Javascript could do lexical closures which meant the client could keep track of the targets, which also meant the server no longer had to care about things servers shouldn't care about.
The next realization was that DIV IDs are global variables and thus in most cases a bad idea, so now my event handlers automatically search upward (and sometimes sibling-wise) from 'this' for DIVs matching classes or other selectors, to keep the scope local.
This TwinSpark library seems like the next iteration of all the good ideas, plus even more flexibility and proper separation of concerns, without JQuery or other dependencies.
Bravo!
Just use Svelte. It's a super simple API, the "trickiest" part is the "reactive sections" but the whole API and reactivity part you can grok in an afternoon.
In the past I have used Angular and React - these days I just always reach for Svelte (speed and simplicity).
In the past I have used Angular and React - these days I just always reach for Svelte (speed and simplicity).
Surprised Solid hasn’t been mentioned.
This: https://github.com/ryansolid/solid
This: https://github.com/ryansolid/solid
Looks like we've come full circle? First server side rendering, then client side, now server side again?
> It supports only modern browsers (not IE or Opera Mini) but drops that 88kb monster.
Whenever I read this, I usually take it the author is being serious. However, how long does it actually take to load a 88kb library these days, is it really 'monstrous'? If this was the authors top performance drain based on profiling, I commend their technical abilities.
Whenever I read this, I usually take it the author is being serious. However, how long does it actually take to load a 88kb library these days, is it really 'monstrous'? If this was the authors top performance drain based on profiling, I commend their technical abilities.
Like what some people said 5/100 on the page speed score is pretty bad. My site used to have this. It boiled down to two things that caused it.
1) I wasn't code splitting by page, and had a couple heavy dependencies (which made things worse)
2) I wasn't pre-rendering my create-react app via react-snap. Meaning during the CI build react-snap runs Puppeteer visits each page on the site, and generates ready-to-go html versions of each page.
Those two changes took me from a 7/100 to a 95+/100 in short order. Makes logical sense too. Now a days the site hovers at 85/100. But I don't have the time right now to reinvestigate it.
With the options at the time, I'm happy with my tech choices. If I had to start with React again today. I would do Next.JS with a static build output. Would save me a bunch of time scaffolding things.
1) I wasn't code splitting by page, and had a couple heavy dependencies (which made things worse)
2) I wasn't pre-rendering my create-react app via react-snap. Meaning during the CI build react-snap runs Puppeteer visits each page on the site, and generates ready-to-go html versions of each page.
Those two changes took me from a 7/100 to a 95+/100 in short order. Makes logical sense too. Now a days the site hovers at 85/100. But I don't have the time right now to reinvestigate it.
With the options at the time, I'm happy with my tech choices. If I had to start with React again today. I would do Next.JS with a static build output. Would save me a bunch of time scaffolding things.
Is this approach similar to Rails Turbolinks?[0]
[0] https://github.com/turbolinks/turbolinks
[0] https://github.com/turbolinks/turbolinks
That 5/100 score is very very strange.
Have you tried migrating your react code base to NextJs?
If anything should be learned from this article is this:
"We need to look at it from two sides: if it’s good for developers and if it’s good for users."
It is a pity this change in thinking required the pressure from Google Pare Rank, but now I see it as something very positive.
Always remember: code is written once, and executed thousands or even millions of times.
No optimization is worse than "premature optimization", and you probably misunderstand what "premature optimization" means anyway.
"We need to look at it from two sides: if it’s good for developers and if it’s good for users."
It is a pity this change in thinking required the pressure from Google Pare Rank, but now I see it as something very positive.
Always remember: code is written once, and executed thousands or even millions of times.
No optimization is worse than "premature optimization", and you probably misunderstand what "premature optimization" means anyway.
All of your issues like dropdown and hover in JS can be solved by CSS, why did you choose to implement them in JS?
About complex logic, it is everywhere. I once worked at an big ecommerce company and the server-side catalog page was more then 2000 lines, nobody want to touch that page. But it would be easier with React since we can break it down to smaller components.
Your 2.5MB minified bundle is actually too big.
About complex logic, it is everywhere. I once worked at an big ecommerce company and the server-side catalog page was more then 2000 lines, nobody want to touch that page. But it would be easier with React since we can break it down to smaller components.
Your 2.5MB minified bundle is actually too big.
[deleted]
Here is an old story from 2016 but I hope this 3-part article is useful. We used React during its early days, to help Aditya Birla with one of their eCommerce websites.
1. https://alarisprime.blog/e-commerce-case-study-building-fast...
2. https://alarisprime.blog/e-commerce-case-study-building-fast...
3. https://alarisprime.blog/e-commerce-case-study-building-fast...
1. https://alarisprime.blog/e-commerce-case-study-building-fast...
2. https://alarisprime.blog/e-commerce-case-study-building-fast...
3. https://alarisprime.blog/e-commerce-case-study-building-fast...
I think there are some interesting points here, though many (including the author) seem to forget: React is a UI library, not a framework. This means it is up to the user to decide and implement everything else, such as:
* Code splitting * Loading of the code (at appropriate times, eg: ) * Any other optimisations
I think the author simply failed to do this well and then blamed the library for their own shortcomings, and moved on to the 'next shiny thing'.
There are plenty of patterns you can use to make your React app responsive, and many tools to help you achieve that, it's just a matter of being able to implement those tools effectively.
* Code splitting * Loading of the code (at appropriate times, eg: ) * Any other optimisations
I think the author simply failed to do this well and then blamed the library for their own shortcomings, and moved on to the 'next shiny thing'.
There are plenty of patterns you can use to make your React app responsive, and many tools to help you achieve that, it's just a matter of being able to implement those tools effectively.
[deleted]
Thought this was a good read - and nice to see the progression of frustration with front-end development throughout the last decade, and then the introduction of mobile. This is a pretty common timeline for web devs who have been doing this ~10+ years.
I wonder why the author did not contribute to intercooler rather than writing his own version. Would it really have been that difficult to avoid inheritance (or add an inheritance free api) and add batching to that library?
I wonder why the author did not contribute to intercooler rather than writing his own version. Would it really have been that difficult to avoid inheritance (or add an inheritance free api) and add batching to that library?
The problem is how you think, and all frameworks is the same, they try to visualize the tree structure. The key to understanding async is to think of functions and state propagation.
Part of me wants to create a hugo template that takes advantage of this (or htmx). You could make each "request" be a rendered snippet. :thinking: I may try this out.
the whole React thing got so ridiculous that you almost cannot call yourself a full stack developer if you don't use it on the front end.
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?