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.
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?
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.
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.
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
> 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.
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.
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.
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.
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.
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.
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].
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.
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!
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.
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!).
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.
>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.
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
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?