Remind me again why we decided to do all this stuff client side? As a server template pattern that seems perfectly reasonable. I don’t see why you’d push that out to the client though, along with a healthy dose of code to make sure it generates identical output in every browser.
I confess that I missed the whole migration to SPAs because I’d stopped doing web stuff altogether for a while when it came into fashion. I’d love to be convinced that this really is way better for reasons I’ve failed to appreciate so far.
If, in fact, that’s true.
Were I forced to write a website today, it’d still end up looking similar to a traditional Django server-side rendering system with pages, and deep links, and all that.
Step 1 - You do it all server-side, and some people start requesting interactivity.
Step 2 - You push a few client-side templates as data, so you can do interactivity.
Step 3 - Your server side framework (not surprisingly) has no support for client-side templates, so you add hack over hack to get the more and more complex you need here and there.
Step 4 - You move those few components into a client-side framework, and interact with them through hacks that your server-side framework doesn't support.
Step 5 - Hell, why insist on using 2 entire GUI frameworks? You move all components into the client-side one and ditch the hacks!
Step 6 (bicycle on the floor and you hurt) - Your server-side framework doesn't interact well with the client-side one, so you move into JS everywhere.
This progression still seems unavoidable, but the web standards evolved almost as much that there's a light visible at the end of the tunnel.
Historically, client-side-rendering came about at a time when server-side compute was expensive. You have to remember what CPU limitations were like prior to around 2010. Website load times were often slow, and scaled poorly with traffic, because anything dynamic needed to be rendered before it could be served. Caching was common fare but load spikes when caches expired were common and personalized content (along with an industry trend towards more and more personalization at the time) added a lot of additional expense.
It's funny how we have come full circle. We are now doing so much on the client that we are often reaching client-side resource limitations. In a typical pendulum swing that our industry likes to go through, many people are saying that the answer is server-side rendering. Many younger developers who weren't working in the industry prior to the shift towards CSR even repeat this sometimes while thinking that SSR is some new tech that's going to save us, unaware that SSR has its own trade-offs and that CSR was itself initially seen as a solution to scalability pains.
Yes, and wasn't mobile a big part of the shift to client side? If mobile users lost their connection, for whatever reason, CSR was thought to be a good remedy for the user experience (UX).
1. People are building actual applications with client side interactivity on the web now.
2. Programmers prefer using programming languages over templating languages. Jsx allows you to mix js with xml and also generally supports better composability than templates (another thing programmers prefer).
3. Some programmers like to use jsx for normal websites with ssr under the spirit of "use what you know".
My naive view is that this type of thing is done so that the server returns structured data from an API call, and then the client renders it however it sees fit. If the client is a browser, then it uses HTML. If the client is curl, then it pipes it to jq, etc.
However it seems like this idea has been taken to extremes and is being overused, and applications that do this for GUI elements are probably just badly designed.
More people to do more building which increases team size and inflates ego? :)
I don't understand the whole SPA movement either and it feels like it just doubles the amount of work needed to maintain anything. I reach for Django/server-side when I can but unfortunately, not many folks appreciate those thoughts.
I can see some perfectly valid use cases but there are times when it's just not needed.
> Were I forced to write a website today, it’d still end up looking similar to a traditional Django server-side rendering system with pages, and deep links, and all that.
That's perfectly reasonable, but JSX et al aren't for building websites. They are for building applications that run in the browser.
You’re right that it’s much, much more difficult to do client side SPAs!
But also, that level of interactivity is pretty much expected now even from layman end users whether they realize it or not.
Requiring a full page refresh is very limiting when building out web apps, for a bunch of reasons. There are a few sort of “hybrid” approaches out there that work reasonably well, but the reality in 2024 is that nearly any web app that sees even moderate levels of success is going to grow in complexity to a place where SPA is the right choice. So we have a bunch of SPA experts building apps that they expect to need SPA levels of complexity…you can do the math.
As a fan of SPA apps and React in particular, I will say they're overused, and something like HTMX is probably a better option at least 2/3 of the time.
Of course, I prefer Redux the hard way over how a lot of these SPA tend to get written in practice.
How do you server-side render something like a chat app? You'd still need a fair amount of client-side JS, right, unless you want to reload the page every time someone hits send. I remember old sites that worked that way.
React is easy and intuitive. No templates, and I can change the HTML however I want in response to what the user does. Not that I do fancy things or enjoy single-page apps, but even if you have that, deep-linking is easy.
A chat app is one of those relatively few cases where React makes some sense.
But, React is easy and intuitive to use for the use case that makes the least sense. Once you get to complex state, I'd argue that React doesn't help much. There are a lot of different tools and ways to manage state, but the way React takes over rendering often leads to weird and confusing bugs, as the 'declarative' syntax is a *very* leaky abstraction.
so, how different is React now from where React was in 2016?
The JS/SPA developers I worked with then would refuse to touch it or any of the other frameworks. We just wrote our own callback functions to handle the interactivity and data updates.
The main benefit is the flexibility to seamlessly move this logic from server to client and vice versa without rewriting all of your code.
Purely server rendered apps tend to have much slower interactions and exhibit weird behaviors so it is valuable to be able to do some stuff on the client.
This is a straw man. There's no such thing as a "purely server rendered app", and never has been (except maybe in the pre-JS days of the internet). At the end of the day, all webpages are produced by a server, and rendered by a client.
The only reason that server-rendered apps are "slower" is because people don't think about what they're doing, and leap right to 100% client-side rendering. Very few things actually need the latency guarantees of client-side rendering.
> Remind me again why we decided to do all this stuff client side?
I would guess easier reactivity and state management on the client. You don't have to do things the jQuery way in manually having to sync your DOM to your data on changes/api data.
You can now have the best of both worlds using something like HTMX or Hotwire Turbo. SPA-like navigation with fast page loads, partial updates, streaming updates, but also HTML on the wire and minimal (client-side) JavaScript.
certain apps benefit from immediate low latency behavior that can only be done in the browser. if you were to build a tool like google docs, for instance, you could not do that with server side rendering.
But this article is about creating HTML before it gets to the client. We clearly couldn’t make Google Docs entirely on the server, but I feel like if we’re creating a list of navigation links on the client from a const list, maybe it’s time to rethink some of that.
Not too strongly opinionated on this one either way but I'd say the array looks cleaner when you can actually see the array; when it's derived from a function or is using much more complicated data it can be quite a bit harder to pull useful info from and can be quite a bit easier for a junior to mess up.
Some of the worst JSX related code I've seen has involved people trying to build up arrays for data like this.
Not to mention, that you might want/need your routing for more than just a navigation display.. but for determining routing, named routes and other links in an application.
I really don't mind JSX in terms of syntax. That said, I've used ASP.Net Webforms, VB.Net (XML Literal Notation) and ActionScript (e4x XML Literal Notation) and others. Even developed a UI toolkit similar to what React became (a decade before), but only supported Netscape (e4x).
I agree with the idea of not maintaining an intermediate representations of things that serve no useful purpose.
But the pattern the author argues against isn't necessarily bad. Certainly not an "anti pattern" or against "the spirit of the law".
Take their own example. An app may very well render a certain set of "nav items" to JSX in multiple ways. In that case it probably makes sense to define the common parts separate from the JSX -- otherwise you'd have to maintain each list of nav items in JSX separately.
I completely agree. I've had to "unteach" this from most React engineers that I've worked with/hired. I agree with the reasons given by the article, and I'd offer an additional reason it's so terrible: you can use this pattern with almost everything. Eventually you end up with a codebase that's really hard to reason about as you're mostly just reading the arrays and not the JSX.
We have a general guideline on this: if the template configuration is static and/or not on the server, it should be plain JSX. This rule has worked out great for us. I think I'm always wary of layers of indirection and needless abstraction, which is why I'm able to catch patterns like this early.
The <li> is at a different level of abstraction. <li> can have DOM attributes applied. For instance, we might want to add [title] to all the items. If you "unrolled" to JSX, now you have to make n changes instead of one.
I mean cool, fine. But are you arguing for less abstraction or more? The original post was about unrolling everything and declaring inline. In a sense, you're now going in the opposite direction by adding another layer of abstraction. I'm ok with it either way. I think all of the above might be reasonable designs in particular contexts.
But an argument like "ok, well then, you're still wrong, just now in the opposite direction" strikes me as contrarianism for its own sake.
My web dev experience is old enough to vote by now, so the answer might be obvious, but how does this work with localization? Would you have a copy of the JSX snippet for each locale? In that case I can see some benefit from separating strings from structure.
In general yea, just use XML to describe your static layouts. But as soon as the collection is dynamic or rearrange-bale, make it a component that maps an array of data and consider virtualization.
It is reinventing JSX in that you are really describing components in JSON.
And of course you are then feeding back those descriptions into needlessly abstract JSX but the primal error was not realizing you are describing components in a bespoke manner.
"reinventing" is a strong and less clear word for "over-abstraction" in my book. Nothing was reinvented in this, and the very example the author gave to prove their point could make sense if, for example, you don't know the menu links ahead of time. Like if they are coming from a database table, this would be totally valid. If we do know the menu links ahead of time then there's no point in abstracting it.
I confess that I missed the whole migration to SPAs because I’d stopped doing web stuff altogether for a while when it came into fashion. I’d love to be convinced that this really is way better for reasons I’ve failed to appreciate so far.
If, in fact, that’s true.
Were I forced to write a website today, it’d still end up looking similar to a traditional Django server-side rendering system with pages, and deep links, and all that.
Step 2 - You push a few client-side templates as data, so you can do interactivity.
Step 3 - Your server side framework (not surprisingly) has no support for client-side templates, so you add hack over hack to get the more and more complex you need here and there.
Step 4 - You move those few components into a client-side framework, and interact with them through hacks that your server-side framework doesn't support.
Step 5 - Hell, why insist on using 2 entire GUI frameworks? You move all components into the client-side one and ditch the hacks!
Step 6 (bicycle on the floor and you hurt) - Your server-side framework doesn't interact well with the client-side one, so you move into JS everywhere.
This progression still seems unavoidable, but the web standards evolved almost as much that there's a light visible at the end of the tunnel.
Deleted Comment
It's funny how we have come full circle. We are now doing so much on the client that we are often reaching client-side resource limitations. In a typical pendulum swing that our industry likes to go through, many people are saying that the answer is server-side rendering. Many younger developers who weren't working in the industry prior to the shift towards CSR even repeat this sometimes while thinking that SSR is some new tech that's going to save us, unaware that SSR has its own trade-offs and that CSR was itself initially seen as a solution to scalability pains.
2. Programmers prefer using programming languages over templating languages. Jsx allows you to mix js with xml and also generally supports better composability than templates (another thing programmers prefer).
3. Some programmers like to use jsx for normal websites with ssr under the spirit of "use what you know".
However it seems like this idea has been taken to extremes and is being overused, and applications that do this for GUI elements are probably just badly designed.
I don't understand the whole SPA movement either and it feels like it just doubles the amount of work needed to maintain anything. I reach for Django/server-side when I can but unfortunately, not many folks appreciate those thoughts.
I can see some perfectly valid use cases but there are times when it's just not needed.
That's perfectly reasonable, but JSX et al aren't for building websites. They are for building applications that run in the browser.
The idea to use it for completely static content came later, mostly because people didn’t want to learn two different frameworks.
But also, that level of interactivity is pretty much expected now even from layman end users whether they realize it or not.
Requiring a full page refresh is very limiting when building out web apps, for a bunch of reasons. There are a few sort of “hybrid” approaches out there that work reasonably well, but the reality in 2024 is that nearly any web app that sees even moderate levels of success is going to grow in complexity to a place where SPA is the right choice. So we have a bunch of SPA experts building apps that they expect to need SPA levels of complexity…you can do the math.
Of course, I prefer Redux the hard way over how a lot of these SPA tend to get written in practice.
React is easy and intuitive. No templates, and I can change the HTML however I want in response to what the user does. Not that I do fancy things or enjoy single-page apps, but even if you have that, deep-linking is easy.
But, React is easy and intuitive to use for the use case that makes the least sense. Once you get to complex state, I'd argue that React doesn't help much. There are a lot of different tools and ways to manage state, but the way React takes over rendering often leads to weird and confusing bugs, as the 'declarative' syntax is a *very* leaky abstraction.
so, how different is React now from where React was in 2016?
The JS/SPA developers I worked with then would refuse to touch it or any of the other frameworks. We just wrote our own callback functions to handle the interactivity and data updates.
Purely server rendered apps tend to have much slower interactions and exhibit weird behaviors so it is valuable to be able to do some stuff on the client.
The only reason that server-rendered apps are "slower" is because people don't think about what they're doing, and leap right to 100% client-side rendering. Very few things actually need the latency guarantees of client-side rendering.
I would guess easier reactivity and state management on the client. You don't have to do things the jQuery way in manually having to sync your DOM to your data on changes/api data.
And SPAs still have pages, and deep links, and all that.
The array pattern is much more useful when data is more complex and has multiple properties, nesting, etc.
Still, one of the big benefits that can't be ignored is that jsx looks ugly and the array looks much cleaner.
If you were to do it as the post author states, it would look like this (bad)
Some of the worst JSX related code I've seen has involved people trying to build up arrays for data like this.
I really don't mind JSX in terms of syntax. That said, I've used ASP.Net Webforms, VB.Net (XML Literal Notation) and ActionScript (e4x XML Literal Notation) and others. Even developed a UI toolkit similar to what React became (a decade before), but only supported Netscape (e4x).
That array list might be generated somewhere else, with much more data and used for multiple purposes.
But the pattern the author argues against isn't necessarily bad. Certainly not an "anti pattern" or against "the spirit of the law".
Take their own example. An app may very well render a certain set of "nav items" to JSX in multiple ways. In that case it probably makes sense to define the common parts separate from the JSX -- otherwise you'd have to maintain each list of nav items in JSX separately.
We have a general guideline on this: if the template configuration is static and/or not on the server, it should be plain JSX. This rule has worked out great for us. I think I'm always wary of layers of indirection and needless abstraction, which is why I'm able to catch patterns like this early.
But an argument like "ok, well then, you're still wrong, just now in the opposite direction" strikes me as contrarianism for its own sake.
And of course you are then feeding back those descriptions into needlessly abstract JSX but the primal error was not realizing you are describing components in a bespoke manner.