Hacker News new | ask | show | jobs
by codethief 626 days ago
> could be a simple SSR site, but frontend devs don't like writing anything but SPAs

It might be worth asking why that is.

I used to write a ton of SSR'ed pages back when that was really the only option we had (before people introduced jQuery and AJAX), then I wrote SPAs (mostly Angular & React), then again SSR'ed websites (Java/Spring/JSP with a ton of more-or-less vanilla JavaScript) and now I've been on a Vue-based stack (w/ Astro/Vite for SSR) for a number of years. And these were all applications with dozens of teams working on them in parallel and a corresponding number of components & LoC.

Personally, I wouldn't want to go back to traditional SSR stacks (Spring/JSP, vanilla PHP, or Python w/ Django or Flask) ever. People often vastly underestimate the complexity of good UIs. They might start simple ("could be a simple SSR site", as you say) and, before long, the wheels come off the bus and they look at a big unmaintainable ball of vanilla JS on top of HTML templates that are directly coupled to the database schema, with global CSS styles permeating (infecting) the entire code base.

In my experience, there is a certain amount complexity that is inherent to a good UI. It doesn't matter whether you use classic SSR, or modern frontend frameworks like Angular/React/Vue/Svelte/SolidJS, or new kids on the block like HTMX/Turbo/… — that complexity will still be there. However, with modern frameworks you've at least got a chance to tame that beast.

More specifically, what modern frontend frameworks give us is: 1) Modularization into components, 2) scoping of styles, 3) type-safety, 4) unit-testability of components, 5) component-local state management (≠ insanities like Redux), 6) clear separation between view/UI logic and business logic.

If you can replicate these features using a bespoke version of JSP or HTMX or whatever, great! But if you can't, I'd personally rather pick a well-established frontend framework like React or Vue. Yes, your developers might still mess that up and produce shitty code, but the chances of them messing up in a classic SSR stack (without the aforementioned features) are so much greater.

1 comments

You're arguing against something different, though: I agree you shouldn't do components with CSS/jQuery, components should be self-contained. I'm arguing against SPAs, where the state of the user's browsing is replicated on the frontend as well, with all the complexity this entails.

Isn't there a good component framework we can use, and that's it? I want to be able to say <carousel></carousel> and have it work, without having to have a whole SPA attached to my components library, with routing, state, and all the rest. Doesn't that exist?

> I'm arguing against SPAs, where the state of the user's browsing is replicated on the frontend as well

What state exactly are you referring to? I'm not trying to play dumb, I'm genuinely curious because in my experience there can be "good" and "bad" state in the frontend (where "good" roughly translates to "inherent to the problem").

> I want to be able to say <carousel></carousel> and have it work, without having to have a whole SPA attached to my components library

A carousel sounds like a great example for something that could be standalone. However, what if 1) the data shown in the carousel needs to be loaded lazily (e.g. because the carousel is near the end of the page and loading the data is expensive), and 2) the carousel's display settings (e.g. how many items are shown at once) depend on the content being shown? I recently had to work on exactly this case. Suddenly, your once static carousel component isn't so static anymore and you have to add a bunch of JavaScript, and also state.

> without having to have a whole SPA attached to my components library, with routing, state

Maybe I'm missing something here but pure React/Vue doesn't require your page to be an SPA or to have any routing mechanism at all?

> where "good" roughly translates to "inherent to the problem"

I don't mean per-page state (that one is inherent to the problem, as you say), but cross-page state (the one typically handled by backends, such as authentication, account info, stuff you've done in your current session, etc).

> Suddenly, your once static carousel component isn't so static anymore and you have to add a bunch of JavaScript, and also state.

You're right, all of this is inherent to the problem as well, and you do need state. Generally, however, I haven't found a need for state to cross pages, which is a really good reducer of complexity (things are easier to reason about if every page load starts with a known state).

> Maybe I'm missing something here but pure React/Vue doesn't require your page to be an SPA or to have any routing mechanism at all?

They don't require it, but they make it easy, and that's the default that all frontend development pretty much starts with. Nobody says "hey, do we really need routing?". They start from "the way you make an app is with routing", and now you have a SPA where you just needed components. That's the big problem, and we've had a fair amount of issues with this...

> cross-page state (the one typically handled by backends, such as authentication, account info, stuff you've done in your current session, etc).

Yeah, that one is a different beast. We've been trying to keep it very simple in our SPAs lately: Account information & other backend data get retrieved from the backend upon page load (its all one big GraphQL query) and then it ends up in the GraphQL cache. If we then do a SPA-like soft page switch, that cache will speed up the next page's page load. However, the next page's GraphQL query will still be fired (it will likely need some other data that's not in the cache yet, anyway) which ensures the UI stays consistent with the backend state. In other words: The client-side cache is merely a mirror of the backend state and its presence or absence only affects loading behavior. There is no cross-page state beyond that (almost, see below).

> Generally, however, I haven't found a need for state to cross pages, which is a really good reducer of complexity (things are easier to reason about if every page load starts with a known state).

Agreed! If I do have a bit of client-only state which I'd like to persist (e.g. some kind of user selection / filter / …), then I simply encode it in the URL. Components then don't keep their own (mutable) copy of that state but simply mutate the URL to modify state. In that way, I end up with a one-to-one correspondence between URLs and UI states.

Yeah, that's a great way to do it, and that way your URLs always work properly as well. Maybe our frontend devs just need a bit more discipline...