Fresh – Next-gen web framework
fresh.deno.dev
fresh.deno.dev
Vercel is doing a really good job with Next, but it's good to see some competition. Of course, that means there's now 65,535 + 1 more way of serving a web page using Javascript (sigh).
Rehydration is a really big deal. Sounds dorky but it dramatically speeds up load times and such by serving flat HTML and injecting JS afterward, like the old days, except you can write code like it's not the old days.
It makes different trade offs and isn’t strictly better by every metric, but overall I’m very happy with it for the two use cases I’ve tried it with. It’s a very low overhead framework once the simple conventions click.
I’d still like to check this out, then redwood and a couple others too. I’m not huge on these frameworks in general, but they tend to have some excellent ideas and smart people behind them, so plenty to learn by experimenting with them.
I use Next not just for the routing and composition and hydration, but for all the other quality-of-life improvements (image resizing, buildchain configs, hot reload), especially when it's paired with Vercel (per-push sandbox builds, stale-while-revalidate, seamless CDN, access to serverless, etc.)
I'm really excited to see how Remix and other Next competitors evolve, but for now, I think it's still the most "full" stack of the React frameworks? Is that correct?
It isn’t too hard to get the same/similar tooling outside of the vercel ecosystem, but it does take know how and a bit of extra time. It isn’t obviously worth it unless vercel isn’t meeting your needs in my opinion/experience.
I mostly play around with other frameworks in order to learn, but for any client where a frontend framework made sense I’d almost certainly choose Next.
Edit: people also get bent out of shape about perfect hydration and shaving milliseconds here and there, but like you mention, Next offers such a comprehensive solution in a situation where routing and hydration are only a part of the big picture. Next gets you really far without any effort up front, which is crazy. I really like it!
Heck, I've heard in a podcadt they're even working on a hosting platform copying vercel.
Arguably there is nothing all that novel in either framework. We are constantly reiterating and rebuilding wheels, doing it a little better each time.
I’m glad to see the Remix take in things gaining steam though. It’s quite a bit less cognitive overhead than Next without any magical trade offs. Nothing ground breaking, but definitely better at things I care about.
Like most things, a blend of solutions would be ideal
You only need one (or none). I personally recommend Next.js for just about anything.
The problem is that that's pretty much every web app. Every time I've used Next I've end up abandoning SSR and building a plain clientside rendered app.
Same with incremental static regeneration and some other features... Next is obviously built not just by, but also for, Vercel. Vendor lock-in already is a small issue and may become a bigger one if they keep going down that route.
Take a look at their middleware. It's designed to be used solely with their serverless cloud BS. It uses a janky JS sandbox which means you can't use node APIs. It's just horrible for no good reason at all. I've never seen middleware so intentionally crippled anywhere before.
And the whole reason you need middleware is to maneuver around the flaws that the prior person mentioned with getServerSideProps.
Next.js probably seems great if you're writing greenfield code in a dead simple web app. Routing/URL handling on Next.js is just broken. This is all glaringly obvious if you've been around the SPA/SSR world for more than a day.
Hydration is actually a compromise, and not a great one for UX. It’s in fact been said to be “pure overhead”, which I think is an overstatement but only slightly.
What you’re describing in the abstract is spot on though. Serializing server state to HTML and sprinkling in interactivity to pick up where it left off is exactly where we should be headed.
And yes it is like the old days, and yes all of HN will rapidly say so. The big difference now is the convergence of code written for both server and client, and compilers which help strip down and optimize what happens in the client.
Hydration in the current sense is re-running most of what the server already did, to recreate the runtime state it already had. That may be perceptively faster in terms of metrics like first paint, but it’s a huge barrier for time to interactive. All the more so when most content is static and has to load twice—fast first as HTML, then slower and redundantly as JS.
The best way to solve this is to not serve or hydrate anything at all unless you need to. The “islands” approach is a very good, but coarse, way to solve this: isolate components which are actually interactive, treat the rest as static. A more granular approach—termed resumability by Qwik and as I understand it the forthcoming version of Marko—works by treating the server-generated HTML as the initial state. The code executed from there is much more isolated than a full component.
How I wish something, anything better could've taken over. .NET was beautiful to work in. Maybe the native apps for iOS or Android are cleaner (I dunno, never tried).
But browsers are what the world use, and they only speak HTML and Javascript (sadly). So we're stuck unless something else manages to create a sea change in how the world uses the internet.
- Primitives already present from the server render are serialized directly into the HTML, and the compiled code reads those values from the DOM
- Everything else is split into fine grain chunks, assigned a special identifier (Qwik uses URLs) which is serialized to the HTML to fetch (which can be eager or on-demand) and activate interactivity as needed
My understanding is that currently Qwik serializes more than necessary—i.e. can be optimized further to eliminate non-interactive chunks from consideration—but that they’re focused on reducing JS cost first.
I don't know whether this means that it doesn't work as SPA any more since if you want to keep the best of both, you will end up creating more4 complexity, a new framework to learn and probably get marginal improvements at best.
I understand the spirit of your comment, but this was/is also true of Google Web Toolkit (GWT).
Is that sort of what Phoenix LiveView does? Return a fully server-rendered page on initial load, then set up a "template" on the client side that can receive any values that change server-side over a websocket and patch them into the DOM?
It’s interesting that the problems with hydration is in some sense caused by the insistence on one-way data binding (deriving the view from the state). I imagine that with two-way data binding then you’d just need to attach event handlers and then the state would be derived from the view on the next interaction. Maybe.
OTOH if there were a way to produce a two-way implementation from a one-way description, that could be great for performance. But this is already more CS than engineering.
I think you can have a two-way binding with a controlled update procedure, akin to one-way data-binding. I’ve read some people claim to do it by producing a kind of one-way directionality under-the-hood (?) from two-way bindings. The goal being that views can update state (and other views).
I think it was here I read it:
«‘2-way’ shouldn't be a problem if a component reports user events (perhaps transformed/mapped) to the domain model without changing it's own core state (disregarding throttling etc.) and only changes its core state in response to events from the domain model.« — peerreynders @ https://dev.to/peerreynders/comment/1objn
Honestly it's unfair to compare a small e-commerce site made with Next to one of the biggest websites on the internet.
If Ebay was made with Next it would probably be much slower. React is the slowest at SSR of the modern frameworks. Amazon considered using it but it was too slow for them so they keep using Java + sprinkled JS.
Ironically we were already doing that 15 years ago years with PHP/ASP/Java/Rails + jQuery.
I imagine you mean that writing (frontend) code now a days is better than how it was in the old days. Well, at least from my perspective that's not the case. "Modern" frontend code requires:
- a package manager (npm)
- node (or deno or whatever)
- transpilers (or is it plugins?)
- TS
- 10K+ dependencies
And to be honest, what is all that good for? To being able to "hydrate" some server-side rendered template? Not good enough reason.
Updates are now easy-breezy (update the server and you don't have to touch clients at all).
There is a place for SPAs. I just don't think your typical grocery store website should use something like that.
Eventually, you'll have enough devs working on this you'll start to run into issues with how you coordinate your work. Your site becomes large enough the code gets more complicated and needs organising to work on it without slowing you down. You'll start to reinvent the above tools to solve these problems.
Frontend/JS tooling isn't perfect, but don't pretend like other languages/systems (can) have a complex pipeline of build tooling.
I didn't see GP make the claim of using it?
Not necessarily, at least in the case of React and Vue.
Rather than using NPM, you can self-host react/vue (or preact if you need something even smaller), or serve it from Unpkg.
You don't need node/transpiler/bundlers if you're only targeting modern browsers. You can use modern syntax, async/await, ES6 import and a bunch of other features with static .js files.
JSX is a tough one, but there are solutions to that, packages domz and HTM are made to allow using React/Preact without NPM/transpilers.
Typescript is also a tough one. But it's also optional, although a good idea to have. I hope optional type annotations land in ES6 soon so typechecking can happen without anything resembling compiler phase.
-
> To being able to "hydrate" some server-side rendered template? Not good enough reason.
The "hydrating" parts is entirely optional in "modern frontend". It also needs some stuff on the server that is IMO significantly more complex than what I described above. But apart from some very specific cases, one don't necessarily need it.
But really, what I meant about "writing code not like the old days" isn't so much about the shitty toolchain (which Next helps with, but I agree it's shitty). Rather, it's the ability to write code like:
<Header loggedIn={isLoggedIn}/>
<Sidebar options={isLoggedIn ? navOptions.loggedIn : navOptions.loggedOut}
<Dashboard>
{widgets ? widgets.map(widget => <WidgetContainer>{widget}</WidgetContainer) : <Spinner/>}
</Dashboard>
Basically, the ability to compose pages & apps out of components (which different developers can work on), and the ability to manage state in a central controller via Redux or useContext to avoid race conditions and the such. That sort of stuff is REALLY hard to do with plain HTML and JS, especially where there are multiple developers involved. React isn't a magical cure-all, it just makes it easy to componentize large apps into smaller areas of concern.
The shitty buildchain isn't a feature, it's an unfortunate side effect of browsers being limited to JS. Essentially it's a "compile" step that became necessary as the vanilla-JS developer experience wasn't able to keep pace with the complexity of desired business apps (and as devs of various skill levels flooded the market). So the developer tools kept growing, but they still had to be compiled/built into JS for the browsers. Next.js makes it relatively painless, compared to how it was just 3-4 years ago. But I agree, I hate that this is a step at all.
Ugh, as for TypeScript... I get that it's a necessary evil, but I spend more time fighting it than actual bugs... coming from PHP, it was already common practice to manually typecheck and coerce everything when needed, anyway. TypeScript often felt redundant and overly sensitive, especially when it came to async nullables, causing false alarms that React could just've silently handled with {isLoaded ? <Component/> : <Spinner/>}
But TypeScript is optional anyway. It's a superset/addon on top of JS, so you never have to use it if you don't want to. If it's scaffolded for you in someone else's project, usually it's just a matter of a either using a .JS extension instead of .TS, or adding a ts-nocheck or similar to that file. Other devs may hate you for that though, when your object or props ends up breaking theirs... so it's definitely a conversation worth having first :)
I started with https://alpinejs.dev/ linked via CDN, and OpenJSCAD, also linked via CDN - I wrote basic html, marked it up with alpine `x-model` and `x-data` tags, and sprinkled a little vanilla js on top. Everything worked well and I got 80% of the way through the project.
In the final 20% I ended up adding a bundler (parcel), so I could bring in an scss framework and override its variables. While it added a fair bit of complexity to the project (dev dependencies, parcel config files) I gained lighter files via parcel's tree-shaking and minification, auto-recompilation/reload during development, and re-usable html partials via `posthtml-include`. I'm also set up to swap to typescript quickly, if the project gets more complex and I start to get annoyed with the lack of compile-time type checking.
So, it's 2022, and you can write a web app without any of the things you mentioned (transpilers, package managers, typescript). Yes, adding even one of them nets you a 100+ dependency node_modules directory - but the reason we keep adding them to our projects is the things it gives us are NICE, and the cost (complexity) is mostly worth it.
Just scrolling list of small blocks of text with small/interactive screencasts/animations that communicate the idea via succinct bullet points.
Much more fluid than the usual approach of paragraphs or breaking the page up into large blocks/sections.
It’s closer to older HTML where you just have text and a scrollbar.
Maybe it's just years of bad habits trained by seeing too many bad marketing sites, where the typical signal to noise ratio is really bad. I guess even when I see a good sales pitch, I don't recognize it as such anymore and try to skip through it... sigh. Sorry, Remix.
https://hackernews-csr.ryansolid.workers.dev/
No need for rehydration, I'd say. Combine this with code splitting for large apps.
It's not a nice experience. Remix (and, most likely, Fresh as well) prevents this.
That build step was there to avoid doing this work over and over!
https://github.com/lucacasonato/fresh/blob/458fe2ca3c12508a6...
https://github.com/lucacasonato/fresh/blob/458fe2ca3c12508a6...
Import maps are pretty cool, you don't need to bundle in modern browsers and it plays to http2's parallel request strengths.
It would be a bit ridiculous to re-compile JSX into JS on every request.
It'll still happen on every cold serverless request, every first request to every scale-up VM, ...
The performance claims made for this new framework need to be proven by benchmarks. Check out this SolidJS Hackernews clone (Client Side Rendered) https://hackernews-csr.ryansolid.workers.dev (by the way, there're other implementations like Remix or Svelte as well)
IMO very hard to beat. For larger apps we can use component based code splitting.
The new SSR frameworks are very complex. So there's a downside to it.
Of course there's a big market for the cloud providers as you need to run and pay for a server for your SSR instead of simply serving static JS! That's why there's such a hype lately. I'm not convinced that the performance gains are worth the complexity, costs or vendor lock in.
Again, check out the SolidJS example app I've linked above and measure for yourself if you really need the additional cost and complexity of a server pre-rendering, hydrating, etc..
That's by the way the reason I was asking for benchmarks. The new kind of SSR frameworks are by the way much more complex than the older template based server-rendered HTML.
He describes it as a post-Unix web framework (i.e. built on serverless primitives like cloudflare workers/deno deploy) with the goal of <10s deployment (which he says requires JIT compilation on first-request)
React is the main driver for JS not some server-side BS.
The classic scripting arg. JS is a poor choice for scripting and hence, not used for it that much anymore.
Are you aware that Deno is very new? I wouldn't say that Ryan Dahl got left behind because everyone hasn't switched to Node yet. There is a large amount of interest in Deno, exemplified by how frequently Deno projects make it to the front page of HN.
> React is the main driver for JS not some server-side BS.
Haha. Just because you hate JS doesn't change reality.
> The classic scripting arg. JS is a poor choice for scripting and hence, not used for it that much anymore.
This statement doesn't make sense. It never was very popular as a Bash replacement, if that's what you're meaning. And otherwise, it is the only option for browser interactivity. So it doesn't make sense what you're saying.
React was the first framework that requires the coder to really understand and utilize the modern features of JS.
Scripting something simple or spinning up a simple endpoint have always been arguments for NodeJS. R always talks about this. Terrible ground for decision making.
Deno has other advantages in paper, like official ts support, all the tooling was written in rust (so it's more performant that the default ones that the others use).
The only downside right now with deno is popularity and maturity of the ecosystem, it is just too new, so you will have hard time finding what you are looking for that works out of the box, while a lot of companies invested in node official packages.
The major difference with Fresh is that it runs everything just-in-time when it is needed, hence doesn't require building no shipping anything by default to the client(but you can still ship some JS for client side interactivity).
The key here is no building (packing, bundling, transpiling). This don't just save time but actually removes the complexity as what you see is what you get. The only things that ships to users visiting your site is around 0-3kb (plus client side JS you decided to ship), not prebundled transpiled polyfilled prebuild 10mb JavaScript.
Since it is Server Side Rendering, the performance is based on design decision.
This 0-3kb includes HTML or some JavaScript runtime ?
How is that possible? In the documentation (https://fresh.deno.dev/docs/getting-started/create-a-route) I see .tsx files... so I imagine that at least one needs to compile TS to JS and then JSX to JS. Perhaps I got that wrong, though and browsers nowadays support TSX out of the box.
I think they all try to solve the same problem: how to get a modern interactive app to run on (and be performant) what is essentially a hacked-together ecosystem, HTML + Javascript, with decades of backward compatibility baggage. The essential problem is that browsers work on the ancient and really poorly designed DOM, but developing against the raw DOM sucks. It's fine if you have a simple webpage with headers and some text, but once you get into stateful UIs, it gets hard to maintain pretty quickly. So there's a mismatch between user experience (in HTML) and developer experience (terrible in HTML, better in other frameworks). So developers of complex apps end up abstracting it away with something like a JAMstack.
So you have things like React, which is essentially a UI library (vs a more fully-featured framework like Angular or even the older Rails stuff, or something like Laravel/Symfony for PHP or whatever the .NET equivalent is). React lets you compose apps not out of DOM primitives but components you define yourself, which in turn are reusable and composable.
But there's a lot of things that React don't handle out of the box: page routing, state persistence, static builds, image optimization, hot reloads, CDN caching and invalidation, etc. A lot of teams end up reinventing all those wheels, or else clobbering together 80 different open-source solutions and 10 vendors. It gets hard to maintain very quickly.
Enter Next.js, one of the earlier successful React-based frameworks. It turns a React app from a quirky UI library into something almost beautiful, because you can now make an entire app, not just a UI, using React and some easy to learn JS config objects.
For example, to make a blog with React, you'd first need a CMS (let's assume you have that part figured out) and an API (also figured out). You can write it as a single-page app, using fetch() or whatever to query the API every time. But then the client has to download that and then render the page. If the CMS is on a different host than your webpages are, it can take quite a while. That whole time your user is waiting, seeing a blank page. And if your CMS goes down, your website goes down, even if the content's been the same for days.
Anyway, you could try to statically bake all that into HTML, but then every time you add a new blog entry or update an existing one, you have to rebuild your project. And then if you want it to be fast, you have to invalidate all your CDN caches.
Next.js essentially takes care of all of that for you, in one easy to use and well documented package. Combined with Vercel (the company behind Next.js, who provides hosting) it also abstracts away all the complexities of the buildchain, CDNs, invalidations, etc.
As a duo, their most powerful feature is rehydration. You can code your app as though it were a single-page app, using React to compose components and pages, combined with file-system routing, to create a whole site. But then you push your changes and that's where the magic starts: Your Next.js server (like Vercel) picks it up, builds it with data fetched server-to-server from the CMS, bakes everything into flat HTML + CSS, and invalidates it across the CDN within seconds. At this point, any user who visits your site will be able to download the HTML + CSS, even with Javascript disabled -- the client does not ever speak to your backend directly. To them, your page is just a static HTML page, served straight from the CDN edge. This means the client doesn't need to load React to see your page. They can have JS disabled and it still shows up normally, it just won't be interactive.
Seconds later after the HTML has loaded, some "bootloader" JS then downloads all the other JS that enables interactivity and dynamic data fetches (comments, etc.)... all invisibly to the user. That is the "rehydration", taking a React app that you wrote and the server buildchain "dehydrated" (baked into HTML + CSS), but then rehydrating it to add interactivity back. Yes, you could do all that manually, but Next.js makes it magically trivial... you never have to think about it, it just works. And it's lightning fast.
So take that rehydration stuff and add on a bunch of other quality-of-life developer experience improvements. For example, images are traditionally another headache, needing something like Imgix or Cloudinary to be able to dynamically resize them on the server (so the el cheapo 320p Android doesn't get served the same 4k retina image). Same with script updates... if you change your SPA, you have to figure out how to invalidate current caches, how to sure visitors who've cached the old version still works with your backend API, etc. Or hot refreshes, or page-by-page invalidations, or whatever. It does all of this in one framework, so you can get rid of ReactRouter, Redux (useContext can handle many uses cases), image processors, Preact, Express, etc. Really the only thing you need to provide is a CMS of some sort, typically a headless one.
That's Next.js (the open-source framework). Behind them is Vercel (the hosting/PaaS company which maintains Next.js). Next is the one I'm most familiar with, but I believe the others are similar (but someone correct me if I'm wrong):
Nuxt.js is to Vue what Next is to React. I believe it's a little less featureful than Next, and it's maintained by a different company.
Remix takes some of the Next.js principles but implements them differently; instead of having a server rebuild and bake your project at push, you can do a similar thing on edge compute/serverless (like Cloudflare Workers directly). It has a really neat feature: nested routes (really more like UI layouts), which are composable UI units that are hydrated serverside, similar to Next.js, and then sent to the client as HTML wholesale. So like your <Dashboard> can include <Widget> and <Chart> and <Comments>, but each one can be individually hydrated, composed, and reused -- all invisibly to the client. My understanding (again, not familiar with Remix) is that this was such an amazing feature that Next.js straight up copied it last month with their Layouts RFC (https://nextjs.org/blog/layouts-rfc?utm_source=next-site&utm...).
Fresh looks like Deno's attempt to produce something similar, but it's still early and not quite as powerful.
If you're a web dev and you've never tried this stuff, I strongly recommend taking a look. I've been coding webpages since Netscape, before CSS was invented. Next.js was the single biggest improvement to my professional life in decades, especially coming from the hell that was Drupal + jQuery. I coupled Next/Vercel with a vendor-supported headless CMS, and the overall developer experience made me fall in love with my job for the first time ever. So much so, I actually switched careers from being full-stack to solely frontend/React focused, because Next.js just made it so enjoyable. No more infra and DevOps hell, it all just works, it's all in JS/Typescript, and I can just focus on coding for UX. What used to take weeks to do in the old PHP + jQuery framework would only take minutes to prototype, a day or two to finish in Next. I hope the other frameworks bring you as much joy!
1. Pure front-end solution (React) they wait on the front-end to handle it all. 2. Remix or Fresh, they are still waiting, it just happens on the server.
It seems like there isn't a significant difference either way - ultimately, you are still waiting, as a user. If the payload is huge, maybe the server model is faster - unless the server is getting smashed with requests, then it'll actually be slower?
[0] https://remix.run/docs/en/v1/pages/philosophy#serverclient-m...
But many sites are more heavily read than interacted with: blogs, news, documentation, even to some extent HackerNews and comments. These are all "write rarely, read often" sites. In those cases, prerendering can be way faster both for the end-user (it's just HTML being downloaded from one single source) and also for the origin server (you build once, CDN caches it everywhere and takes over from there). Typically, the tradeoff is that it's also a PITA to manage these buildchains, especially once you get into obscure webpack or babel configs. Next.js handles it really elegantly.
Next.js has other benefits too, especially when coupled with Vercel. It is more than CRA + static builds, and even if you never end up using the rehydration system, the routing/image optimization/per-commit preview sandboxes may still be helpful, though not life-changing. For me the killer feature was being able to detach the data layer from the (write-rarely, read-often) frontend, such that the frontend could always just assume it would have access to the latest data from the API (because Next.js takes care of that).
To give you a before-and-after comparison... I worked on this page previously: https://www.fieldmuseum.org/exhibitions
That version is running on Drupal. Some of it was in a Drupal template, some of it was in-house PHP. To fetch data, we had to use a mix of Drupal built-ins and some raw SQL, mixed into some ugly templating language. Then through custom modules we had to add jQuery and React, sprinkled on top. Drupal had to "build" the page into HTML whenever we save, and then a separate buildchain would add back the Javascript on top of that and try to bundle it all. The developer experience was ugly and required needed at least four languages (Drupal, PHP, jQuery, React). Filtering is done serverside, so filtering by e.g. Type = 3D movie requires an API call and takes several seconds. If you turn off Javascript the whole page breaks and you can't access any of the links anymore. This version is pretty fast thanks to in-house optimizations and extensive caching by Pantheon (a specialist PHP host), otherwise it would be really, really slow. Our dev and staging machines were hell to use because every page took like 10-20 seconds to load.
The new version (WIP, and I don't work there anymore): https://nextfield.vercel.app/exhibitions
That is 100% Next.js/React and only that, no more PHP or jQuery needed and no other frameworks. Data was moved to a headless CMS (DatoCMS in our case, which returned convenient GraphQL responses). It is slightly smaller over the network. All filtering is clientside and instant. If you go to a different exhibition page, it just has to load the JSON data for the new exhibition (text and image URLs + thumbnails), in like a 25kB JSON instead of the whole HTML page (headers and footers and all) all over again. The images are resized on the server for your viewport needs before they're sent to you (though TBH I am not a big fan of that feature because it's not preloading images right now). Even if you had JS disabled, the page would still load and the images and links would still work, you'd just lose the interactive filters.
So for users, the new version is hopefully a bit faster. The real improvement was in the developer experience, being able to code everything in React and not have to think about how it's going to get rendered into HTML or how we're going to balance our caching strategy (invalidations vs not overloading origin). And devs didn't need to use PHP at all anymore.
CRA wouldn't handle much of that, it'd just serve up a SPA run by a single server.
The Drupal version is seeing production traffic, while the Next version only sees a few devs now and then.
Anyway, with a bit of discipline and some frontend tricks (preconnect, preload, async) you can create a Drupal page that's super fast. And it would actually serve less code to the end user than a page based on JS frameworks (as all things happen on the server).
Example: https://www.magneticpoint.com/
You still need a place to store actual content/data, of course. But that could be any store or service that gives you an API endpoint to fetch from.
The HTML it returns is really big! 374kb before compression. I turned off Javascript to see how the page loaded without it, and it still downloaded the whole 374kb HTML (I guess that's to be expected).
I dug into the HTML and realised that the reason for the size is that Next.js includes a <script id="__NEXT_DATA__"> element that contains the entire data of the page's components as JSON for React to hydrate. This JSON takes up 60% of the whole HTML file! I suppose it's prioritising smooth rendering over reducing data use, but it doesn't seem very optimal, tbh. And it's really inefficient for browsers operating without JS, since every navigation click requires downloading a bloated HTML file.
I found Github issues, SO questions, and blogposts about this drawback, and it seems like this is a common practice for even the largest websites. This massive duplication seems to be a problem that newer frameworks are trying to solve. I wonder if non-dynamic sites like this aren't better off just being static HTML served from CDNs, since they aren't interactive.
Also, the main stylesheet is 1.12mb uncompressed! Bootstrap is part of it, but it seems like there's a lot of other CSS?
(Now I understand more about why my web browsing on mobile consumes so much data even on low-media sites, ouch.)
1) That 374kB file is only like 50kB gzipped. We had bigger fish to fry/optimize, like that massive CSS. (Which is a holdover from the PHP days. Phase 2 of the project would eventually have refactored that and tree-shaken it, but it was out of scope for the prototype.) A lot of the bloat you see is because that's still a work in progress and they haven't had a chance to do any optimizations yet. A future version would probably get rid of much of that CSS and maybe even Bootstrap, then think about webfonts, oversized images, etc. There's a lot of work on that front.
2) Our target audience wasn't expected to have Javascript disabled, and with it enabled, Next's hybrid rendering model actually makes it really fast to navigate between pages after an initial page load. In an earlier test, we had the individual exhibition pages side-by-side, the Drupal version next to the Next one in iframes, plus back/forward buttons to navigate through each exhibition in sequence. Originally that was meant just as an easy way to catch visual differences, but we realized that the Next version was loading almost instantly (couldn't even see it load), whereas the Drupal version took 4-5 seconds per navigation. It turned out that Next was able to just download the tiny JSON (~20kB) for each exhibition and injected that into the React shadow DOM for updates; it didn't have to download anything else while navigating between navigations. That was an unexpected, and really powerful, feature that would normally only be available in SPAs.
Realistically, this means that if users had JS on, the initial page load might be a bit bloated, but subsequent navigations between pages should be MUCH faster.
3) The JSON shape was also an artifact of our CMS API (in GraphQL). If we wanted to, we could've optimized it before sending it over the wire.
4) Most importantly... I want to reiterate this... the main benefit of Next.js is NOT page performance, but developer experience. To the extent that there are any improvements at all to page load speed, that is a nice side effect. But even if there weren't, it would still be worth it for us. The big difference is in being able to quickly create new pages/templates, in a single language (JS), with a zero-stack configuration. Especially once we also decided to move the content to a hosted headless CMS. That meant no more LAMP stack to maintain, no more DBs to prune, no more fiddling with CDN caching, Drupal modules, Docker, CI/CD etc. Previously we were spending 80% of our dev time fighting our own stack and framework (Drupal is really really hard to work with, even compared to the messy Node/React ecosystem). Next got us out of the DevOps and infrastructure game completely so we could focus solely on the frontend, and Next + Vercel takes care of everything else. It's not just "serverless" but almost stack-less in a way (managed). All we had to do is write React, push a commit, and done. For a small team, that was HUGE... being able to push out a new template in a matter of hours instead of days/weeks, and being able to deploy to production in seconds (and roll back in seconds) too. In the Drupal world, there are companies like Pantheon and WPEngine and Acquia that try to do the same thing for the PHP landscape (and they do it well), but Next + Vercel is waaaaaay easier and faster. Other competitors in that scape (for JS) are Netlify, Gatsby, etc. These Jamstack hosts are gaining popularity because they are so much easier to work with than the traditional backend + pipeline + frontend model. But of course they have their own tradeoffs and aren't right for every use case -- for example, if we anticipated heavy user interactions (logins, profiles, personalizations, etc.) we would've had to consider a lot more factors. But for write-rarely, read-often informative website, it was the perfect fit for us. YMMV of course!
So until that happens the UI looks functional but isn't. The user is left to tap/click furiously on that button but nothing happens?
It sounds to me like this is only suitable for pages where interaction is an exceptional thing that users will only attempt to do after reading some content.
There might be cases where the issue you describe occurs, but I haven't actually seen it in testing. If it's a concern, you could of course add throbbers or the such. But generally, in our limited tests, it hasn't been an issue.
Still, it is an issue they (and the React ecosystem) are actively working on improving, with React Suspense (https://17.reactjs.org/docs/concurrent-mode-suspense.html) and Next.js Layouts (which I understand is copied from Remix(?) and can fetch component groups in a hierarchy https://nextjs.org/blog/layouts-rfc). There is also server components and streaming, which can render HTTP snippets/components (instead of pages) and send those back over the wire, similar to the PHP days: https://nextjs.org/docs/advanced-features/react-18/streaming
But again, the benefit is mostly to the developer experience. It's a lot easier to write everything in React than to have to switch between PHP and Drupal (as in the admin UI) and JS. There are some benefits to the user experience if done well, but the same could be said of a statically cached Drupal output.
Actually I think the big trend in JS front-end development is realizing that you do have to think about it! React rehydration is often a very slow step (whether it's Next.js or anything else, I don't think it makes a difference), definitely not "lightning fast" on non-trivial apps with lots of data. Islands architecture goes a long way towards solving that but it's still a bit limited today.
[0] https://twitter.com/dan_abramov/status/1200118229697486849
There are some ways to work around that, if it actually turns out to be an issue (which it wasn't for us)... discussed it a bit more in my other response: https://news.ycombinator.com/item?id=31727249
Where does Next fit here?
That would be Blazor for anyone interested in checking it out.
2. How is solved the situation when <button> is delivered to the user, but "onClick" action is not because network failed?
I can't speak for all frameworks, but fresh can dynamically break out shared dependencies so you don't have to download the same code twice.
> How is solved the situation when <button> is delivered to the user, but "onClick" action is not because network failed?
Developers need to deal with this in their applications. The counter example on the fresh homepage uses <button disabled> for the server side render, and only enables the button in the client side when the counter island hydrates.
Seems like a departure from the norm for no reason unless there's some Deno particularity about it.
This just may be the Deno standard for their import mapping functionality since they also do full URLs for imports like Go (sans schema) normally.
Deno is also just not exactly like JS ecosystems, and that's exactly the point too. Opinionated defaults, out-of-the-box support for TypeScript, death to NPM.
I like that in the last couple of years the developer experience was improved in some ways, this kind of apps that you could develop "easily" as monolithic could be served in a serverless hosting as a microservice (if I understood correctly how serverless works) serving each endpoint as a separate service, everything without you as dev put too much effort into it.
They're two different spins on how to render content. They both have pros and cons. It's good to know this and make an informed decision about which strategy you take.
Anyway, the change happened because of real environmental changes. Developers everywhere didn't just wake-up and decide their old values were the exact opposite of the truth, both opinions stand on valid models of the world and hard-acquired empirical information.
Are you equally cynical about the hundreds (thousands?) of different ways to setup and deploy infrastructure (all with different pros/cons)? FE is timid compared to devops churn.
I find this very interesting. I get that adding a build step can be a pain during development / deployment, but running your TS build once per deploy seems _much_ more efficient than doing it repeatedly, as needed. Or does it get cached , so it's built at-most-once? I haven't really dug in.
I kept the demo repo around here, in case it's helpful to anyone: https://github.com/lewisl9029/buildless-hot-reload-demo.
The repo itself is quite out of date at this point, but my current project, Reflame, is essentially the spiritual successor: https://reflame.app/
Reflame has the same ideals of achieving the developer experience I've always wanted for building client rendered React apps:
- instant production deployments (usually <200ms)
- instant preview environments that match production in pretty much every imaginable way (including the URL so we don't have to worry about special whitelisting for CORS and whatnot), that can also be flipped into development mode for fast-refresh (for the seamless feedback loop we're used to in local dev) and dev-mode dependencies (for better error messaging, etc)
- close-to-instant browser tests (1-3 seconds) that enable image snapshot comparisons that run with maximum parallelism, and only rerun when their dependency graphs change, and auto flake detection/recovery
> "The framework uses Preact and JSX for rendering and templating on both the server and the client."
Really nice to see Preact used here, it's a much more rational choice than React if you were planning on using React anyways.I set Next.js up to use Preact as the engine but it takes a bit of config work to do this and isn't an officially/OOTB supported feature.
It's basically what the original "isomorphic" javascript promise was to have the same code seamlessly running on server and client with a flexible split on what part ran where, now possible down to an individual tag/component.
Also worth mentioning projects like .NET Blazor, Phoenix Liveview and Rails Hotwire that approach client interactivity through their own backend languages instead of JS, usually with some partial refresh mechanism using AJAX or websockets.
Anyway, you're missing the crux of my point. I was criticizing GP for implying that all NodeJS-based frameworks require a dynamic server to run respond to queries at runtime. They don't.
Remember PHP was a dominant paradigm for a long time. (I actually don't dislike PHP that much myself but it is a good example since many dislike it intensely.)
Edit: JS, until TypeScript, had almost all the disadvantages of PHP (bug prone syntax, lack of typing, inconsistent ordering of method parameters etc) but not PHPs advantages (shared nothing, fast dev cycle). The only advantages JS had was that it looked cleaner and that one could use the same language frontend and backend.
I think you have just summoned Cthulhu.
It's a sound idea. Ideally this would fall back nicely when JS is disabled to good old get/post requests. It'd enforce a stricter mental model of what's a "page" and the boundaries between them. Currently, with JS component libs, this concept is somewhat blurry.
I use Flow, pretty simple to use and it's included with setapp (an app subscription platform on mac).
You can also check out svgator, it's an online based solution. https://www.svgator.com/
Cheers!
Good that classic web frameworks are still around and healthy, which have been rendering templates on the server side for a decade or so.
Of course, using React has all the issues of SPAs, and trying to use something like Next seems somewhat fiddly due to keeping client-side and server-side state in sync and managing hydration. I'm entirely in favor of a framework that allows writing a server-rendered app with a decent templating language (and ideally a good path for writing client-side interactivity if I need it); it looks like Fresh might do that. I'd love to hear about other frameworks (JS/TS or otherwise, though I definitely prefer statically-typed languages) that match up with what I want, though.
This is exactly what I find frustrating about go templating. I lose autocomplete and type hints.
Afaik `tsc` deals with `.ts` files and nothing else, making typescript and `tsx` actually 2 separate languages, as the name suggests "typescipt extended", but maybe I am wrong.
> TypeScript ships with three JSX modes: preserve, react, and react-native. These modes only affect the emit stage - type checking is unaffected. The preserve mode will keep the JSX as part of the output to be further consumed by another transform step (e.g. Babel).
So the only non-React dependent mode would be "preserve", which just keeps it like it is. The other modes of compilation/output would need to be processed by React.
On the other hand: OK, it can be processed by tsc, so technically, they have built-in some React support into TypeScript. Not sure I am a fan of such framework specific features in a compiler. Would have been nicer to have an actual standard for web components, which is not dictated by "this is how React does it" and which can be output without being framework specific. Then each framework can choose to interpret that output however it wants.
I get that. The thing is, that you are already using a "new language" when writing TSX/JSX/whatever. The philosophy of it does not even try to prevent you from making any mistakes. It is like in Python Mako vs Jinja2. In Mako you can run arbitrary Python code easily, so you need a lot of discipline to not screw up. In Jinja2 you are mostly just handing in the data to render the template, perhaps apply some filter or so. This help inexperienced developers to keep clear boundaries between the view layer and other layers. But with TSX/JSX/etc. all that is thrown out of the window again and people will do anything they want in a place, where there should be view code only.
Whether I need to learn which elements can be inside which other elements in TSX/JSX, as it is not HTML + JS or anything and it does not allow mostly (!) arbitrary nesting like HTML, or I learn a templating language, which is more traditional ... I have to learn a new little language (perhaps DSL, or whatever one could call it) anyway.
And with the swing back to server-side rendering, the question arises, whether we really should mix all that state and behavior together again and forget mostly about the lessons of the past.
The deno runtime is very interesting.
Most intriguing: no build step. That's a big difference from sveltekit which takes very readable input files and produces hardly readable files.
- The dev experience is closer to the early days of PHP.
- TypeScript, Preact out of the box. No need to configure build tools / deploys much faster. It's a pain in the ass to make these working at the same time and targeting both browser and server nowadays.
- You can have interactivity without bolt-on client-side scripts that are different from other parts.
- The code could be running on the edges.
- I'm not sure what does the island based client hydration means, but sounds like Remix
Many other frameworks could do some of them but not all (Ruby needs JavaScript/Turbolink, Next.js need to build then refresh, etc)
The islands approach is different: pages are server rendered, but you can easily define islands of interactivity (like, say, an auto-complete search bar) where just enough javascript is sent to make those components interactive—and only when it's needed. You can control at what point exactly each component is made interactive: as the page loads, when the component becomes visible, when the user first interacts, etc. It's a great way to balance performance and rich interactivity. If the user never scrolls down to your photo carousel at the bottom of the page, the javascript is never requested.
If this sounds like what we used to do with say PHP & JQuery, you're not wrong. The difference here is we have the same javascript-based template logic and component model both clientside and serverside.
Some other projects adopting the islands pattern: https://iles.pages.dev - https://astro.build - https://slinkity.dev
More reading: https://jasonformat.com/islands-architecture/
Ooh. We have supported this as a feature in the Qbix Platform since 2014: https://qbix.com/platform/guide/tools
Back then, it was called “progressive enhancement” (anyone remember it?) before they moved to “graceful degradation” where JS was assumed always on (like broadband internet became assumed always on, instead of previous generation stuff like IRC that expected netsplits, now everyone just had SAAS on The Web with online/offline status).
What we always advocated for is to build client-first software that works with JS, but then spend time to make versions that render on the server and work without it. In that order. Flips “progressive enhancement” on its head:
https://qbix.com/blog/2020/01/02/the-case-for-building-clien...
I gave a talk on this when I worked at Lab49, it might be more interesting in video form:
It is using silly naming conventions in filenames as an alternative to specifying routes. eg /users/[name].tsx for /users/:name
It uses a manifest file. I remember when entity framework in C# had a separate file that represented the mappings of the database. The problem is the database and the manifest would get out of sync. I imagine the same thing would happen here.
There is no documentation about the islands or how they are implemented.
It uses JSX, which I don't think is even very nice.
It seems a lot of this "going back to server-side" is due to the slow and bulky nature of loading react on page load, but libraries built on top of native web components alleviate this issue to some extent. For many people, farming the rendering off to clients is faster if they don't have enough server capacity. After the first page load, client-side is faster. I am not sure how many people benefit from going back to server-side. I do think initial page load is very important though.
It says there is no build step but there has to be one because v8 runs JavaScript not TypeScript. The documentation for "swc" compares it to babel...
Its a convention also used by Next.js, so it is just sticking to existing conventions.
> There is no documentation about the islands or how they are implemented.
Documentation is still only partial & incomplete.
> It says there is no build step but there has to be one because v8 runs JavaScript not TypeScript
This is a module for Deno, which does that under the hood, it isnt something a deno user would have to be concerned with.
At least next generations will start write JS/TS on both ends (front/back) and hopefully maintain seamlessly the state, benefit the server side and benefit on the front. Writing in same language and sharing the logic around.
Sometimes it is a nightmare switching between languages python/php/go/anything else and then js for full-stacks.
> "To include this in a page component, one can just use the component normally. Fresh will take care of automatically mounting the island component on the client with the correct props:"
How does a developer know what rendering is going to take place client-side vs server-side, and is there any way to control this?Or is it all fully "magical"
What I read from other comments is that fresh does code splitting, so for example if the interactivity is out of your viewport and you scroll down to that element, it will then load the js required to make it interactive, while I find that idea cool, what worries my is that it could take a few ms to load the js and then other few ms to boot that component and render it interactive. But I don't have experience with any of the frameworks (besides next.js where I build a small demo project to try it out).
> Routes are defined as files in the routes directory. [...] If the file name is contact.js and is placed inside of the routes/about/ folder, the route will handle requests to /about/contact.
I can't say I'm particularly keen on being told what directory to put files in, or or being told that I must only code exactly one endpoint in each file. Why aren't I allowed to code multiple endpoints (e.g. for related functionality) in the same file?
Also, in what directory do I put my files if the url is of the form /user/{username}/page/{pagename} ?
So the example above could be:
user/[username]/page/[pagename].tsI have no idea why this is the default behaviour, it sure feels something like a lot of people put a lot of work into and by then they didn't realise their fancy new feature doesn't make things better.
But I’ve noticed that it makes it easier to quickly understand new codebases in my organization. Everyone are using Next.js and all the apps have about the same file layout.
I think it’s neat that having extremely fast project dir fuzzy finders has influenced framework design. Like how Django’s design is heavily influenced by how importlib works and would be completely different if Python modules worked differently.
Also, given a filesystem config, now I'm forced to have many very small files around for each route where each file is most likely just a call to another service handler. I'd prefer to mash most into bigger files that handle related but distinct routes.
Less important, but comes up, it's nice to be able to match routes based on code and not just string equality ... e.g. everyone seems to like having routes for usernames start with '@'
Add an Express route -> tell it to render a component/HTML via a res.render() function -> it auto-hydrates everything on the client for you. Components are written in plain HTML, CSS, and JavaScript (no JSX or other funky syntax) with a quick-to-grok API.
No new concepts to learn and keeps you as close to the core technology (HTML, CSS, and JavaScript) as possible.
- Just-in-time rendering on the edge.
- Island based client hydration for maximum interactivity.
- Zero runtime overhead: no JS is shipped to the client by default.
- No build step.
- No configuration necessary.
This honestly reads like satire. It sounds like something on the sarcastic VanillaJS homepage.
This gibberish being the second bullet point in a list of core features is a huge turn off. I know it’s tongue in cheek, but still
I think it's mostly like a tongue in cheek acknowledgement of how everyone in big techs is fighting so hard for a promotion that they aggressively brand the heck out of every library, exploit, and "new tech" they scheme up, even if the thing they're mentioning is using weird language for a concept that isn't new.
* Island refers to this https://jasonformat.com/islands-architecture/
* Island based hydration is a form of partial hydration where the boundary is the component islands.
EDIT: oh, if it's made by a Deno employee that changes it.
In the jquery aprroach the in-memory model is built straight from what's on the page (the dom). This removes an entire class of problems (there's no mis-synchronization possible) but has its own set of issues (the model you build may not end up being what you expected while coding).
More importantly, "hydration" use implies that the HTML that gets sent to the client is a Server-Side Rendered version generated from the same JS code that runs in the page. The very big advantage here is that there's a single place where the page's content is controlled from (the js/jsx/whatever code) instead of having to manually maintain the html/js relationship by hand like in the jquery case.
Furthermore, the jquery approach is global in nature (you can manually scope jquery-fiddling, but if you make a mistake you may end up modifying stuff elsewhere on the page) whereas this cannot happen in the vdom approach.
In general, the vdom approach makes code more manageable at the cost of runtime performance. That's why many people recommend react and/or other vdom-based frameworks for the complicated cases (lots of dynamic stuff on the page) but vanilla/jquery/etc. when the in-page interactivity is lower.
Today's crop of "frontend frameworks" (such as nextjs, fresh here, etc.) are trying to achieve the benefits of the vdom approach while minimizing its' drawbacks (and full-page hydration is a big drawback because it takes "a lot" of time decreasing the page's Time-To-Interactive metric).
The point is that you don't do this. You do:
const App = () => {
// Client-side counter
const [count, setCount] = useState(0);
useEffect(() => {
setTimeout(
() => {setCount(count+1)},
1000
);
}, [count, setCount]);
// HTML-rendering part
return (
<div>Count: {count}</div>
);
}
The framework (next, fresh, whatever) grabs this and generates:- Some sort of "bundle.js" that contains the component (as well as the framework to render it).
- An "index.html" that contains "<div>Count: 0</div>" (and references the above bundle).
- When the "bundle.js" is loaded, it runs the App component in the browser and reconciles the in-memory vdom with the browser's dom (that has the div because it came in the .html)
- From then on, the component runs normally (i.e.: the counter keeps increasing and you see it increase on the screen).
In vanilla-land, you would:
- Write "<div>Count: <span id="count">0</span></div>" in an index.html that also loads a "counter.js"
- Write the counter.js file with something like:
document.addEventListener('ready', () => {
const count = document.getElementById('count');
let value = 0;
setInterval(() => {
count.innerText = value++;
}, 1000);
});
Less code, you don't need frameworks, hydration nor anything similar and it's probably much more performant.However:
- If you want to change the counter's initial value, you have to update both the html and the js file
- If another dev adds some element with id="count" to the html the whole thing breaks.
- If you want to modify the generated html structure, you have to edit both the html and js file
- When the logic gets more complex, you will have to implement it both in your backend-code-that-generates the html as well as in the js
That is, even though it doesn't seem like it in a simple example such as this one, the developer ergonomics of having a vdom are much better when things get hairy. The million dollar question then becomes "how can we achieve a performance and code-size similar to the vanilla approach while keeping the ergonomics of the vdom".
vdom keeps being mentioned, but it doesn't matter here because it's implementation detail, other frameworks will do it differently.
I don't see it's ended up being different than js as yet another backend language with a separate js for volatile state on ui.
Hydration in context here means taking some HTML that was server-side rendered with JavaScript and then, in the browser, handing off subsequent rendering (in response to user interaction) to JavaScript running on the client.
Think of it like those little dinosaur sponge toys that would inflate when you poured water on them. The dry sponge is your server-side rendered HTML and the interactive JavaScript is the water being poured on it.
Instead they create new hacks.
Island based client hydration, right.
["interactive islands"]: https://fresh.deno.dev/docs/concepts/islands
This will be great for mostly static pages.
Docs should include obvious link to github repo: https://github.com/lucacasonato/fresh (edit: it's already in the footer)
Also, deno/x needs an update: https://deno.land/x/deno_fresh
Certainly everyone who creates a (somewhat serious) web framework is at least aware that they could also participate in the development of an existing on. If they feel there is something to gain by going from scratch, why not? I don't think the popular existing frameworks exactly suffer from under-contribution.
My point was that it's fine if someone starts working on a new framework, but personally I would rather have all that effort be used for developing services or tools that are actually needed and do bring value to the community.
EDIT: To make it more clear, the issue is when the creators of such frameworks do believe they are going to change the world by recreating in a slightly different way something that already exists. If they are doing this for fun or to get experience, it's all good, but very often those frameworks do seem to have the ambition of becoming the next big thing and revolutionizing the way people are developing web apps.
Why not tagged template literals, like Worker Tools[1] and lit[2]?
[1]: https://workers.tools/html/ [2]: https://lit.dev/
It's very clean and simple, I like it.
https://github.com/lucacasonato/fresh/pull/108/files#diff-27...
Although the way it's implemented seems similar to mkdocs.
https://fresh.deno.dev/docs/getting-started/adding-interacti...
It looks identical to React.
Is this ling that I'm missing? Or a lark?
I'm just balking a bit with the word 'hydration' entering into some kind of normative lexicon.
I think there is probably a better word for that.
or is it like anyone can host anything on deno.dev, similar to js.org?
[0] https://github.com/lucacasonato/fresh/blob/main/src/server/d...
There’s companies that bet on deno/deno-like runtimes. It’s a move to having more options/control on sandboxing/embedding JS on server runtimes.
Think of how Lua is used. Heavy lifting in a compiled language with Lua on top to cover app specific logic.
Node is in a sense that, but with a stronger emphasis on being a general purpose runtime for JS.
Deno (and similar), gives you more options for embedding and restricting the JS side.
I think it’s worth keeping an eye on, because I think the JS world is soon in an refinement/optimization phase. It’s settling and stabilizing towards a set of core ideas and Deno might be part of that.
Software development already has its own vocabulary, but I feel quite ignorant now.
Let me try:
Hair dry volcano hydration off the cliff only, no added sugar.
For anyone wondering what it means: it ships only the JS for select components (usually ones that have some sort of client-side interactivity, such as the incrementing counter on their demo page), so it only has to hydrate that part (where hydration is reconciling the client bundle execution result with what is actually on the page).
This is opposed to the standard way React works which is the entire JS used to render the page—even the static non-interactive bits like plain HTML—is shipped to the client and all your function component functions are run (just without the actual inserting DOM elements again if SSR is used).
React is currently developing server rendered components that works with streaming SSR introduced in React 18 that delivers this same functionality. Basically your app can then be composed of server components and client components (essentially what all current react components are), and only the JS for client components is actually sent to the browser and hydrated.
Isn’t the difference here that with React Server Components you’re still fully rendering client-side, but you can ship the data alongside the JS? Whereas with Astro / islands of inactivity / partial hydration you ship actual HTML and then after it renders you make interactive (hydrate) only the relevant parts?
It wasn’t universally that bad but at its worst it was totally this bad.
So it's kind of best of both worlds and it makes perfect sense.
I like to think of it as a Pendulum! Back and forth it goes.
I'm just not a huge fan of SPA in most cases. I had to commute a few years ago everyday and it was near impossible to read any news website, because their stupid React or Angular SPA toy just decided to stop loading. Mobile reception isn't that great in Germany, when you're sitting in a train. The weirdest thing was to watch the content load and then like 10 seconds later going blank, because the connection timed out.
Your intuition is correct.
The "SSR" looks confusing because people are using that in 2 different ways:
(1) Server-Side-Rendering means using Client-Side frameworks like React on the server : https://en.wikipedia.org/wiki/Server-side_scripting#Server-s...
...or...
(2) Server-Side-Rendering means _any_ dynamic page generation on the server side regardless of programming language or framework. That has been done since the beginning of the web with Perl+CGI, PHP, ASP.NET, etc. For this mindset, SSR means "we've gone full circle" because it ignores the Javascript evolution from clients to servers.
The 2 groups are talking past each other. For people using "SSR" definition (1), it's doesn't look "full circle" because a language like PHP was never in a runtime in browsers so there was never a client-side-to-server-side re-use of the rendering code. "SSR" is probably a bad name to describe that trend.
So you have one view library (and routing/models etc) and the innovation is that a) it very intelligently only delivers the bare minimum JS of what’s needed to make a fully interactive front end app (which is why you needed React/Vue in the first place due to limitations of pure server side code) plus b) using the same patterns and awesome libraries everywhere on your site, not just specific interactive components but blog, about pages, admin panels, etc.
So you’re not doing python jinja or Ruby ERB templates/helpers/routes/etc on the server mixed with tons of duplication with React/Vue on the client.
Exactly. As a PHP guy I'm not thinking in terms of SSR or CSR, but backend and frontend. PHP never had to do things on the client side, that is what HTML and CSS are for (and some JS). JS apparently has to go from client side to now also server side.
I don't think it's necessary an advantage to have one and the same language doing both the backend and frontend. I also don't know of any disadvantages, I have no experience with JS frameworks.
But everybody is hitting some very interesting points in this whole comment thread. You have some smart remarks about talking past each other. And I also think your observation of PHP never having to run in the browser is a sharp observation. Thx.
With each new wave - we get people that either lack the time, the willpower or the conditions to understand what came before them (the ground they're standing on), and this is how we end up rediscovering things every 2-3 years. It's much more tempting to just discard old knowledge, reinvent from scratch.
People were doing "SSR" with PHP on the server-side 30 years ago, and still do today (can you imagine just how much progress that platform has made, and how much collective knowledge has been developed around it?). It's just not hip anymore, because it wasn't invented within the past 36 months.
Then having one language, no, one _shared code base_ for both client and server, which automatically can only send the minimum necessary over the wire? That's a _huge_ step forward.
And I say this as someone who was developing CGI scripts before PHP came along.
This is exactly the kind of non-nuanced, buzzwordy and handwavy advertising I was ranting about.
1. Why should having the client and server share the same codebase even be a goal in the first place? This should be a nuanced conversation with different trade-offs, there's no one-size-fits-all here. It's disingenuous to paint this as an ideal we all have to work towards on a whim.
2. I don't even know where's the innovation in being able to "send the minimum necessary over the wire". This is a pillar of decent software engineering. We've been severely regressing here due to the pervasive use of bloated abstraction layers, combined with a deep lack of understanding of how things work (ref. the Uncle Bob post I've linked earlier).
Because, in building complex and dynamic web apps, the alternative is to repeat a lot of the same logic in both frontend and backend.
Of course if you are building a blog or a simple static page this is not useful, most of the new techniques are a response to more advanced requirements.
For instance, right now I'm building a "buy form" for internal use (by employees only) in a shop. There is a ton of domain login used to select which field to display, what validation login to use, how to autocomplete some fields ecc ecc. The system is a PHP/Laravel server with SSR rendering and vue only used for some specific advanced components. Most of the logic inside the form has to be written two times: in PHP and in vue. Most enum types are repeated (and must be kept in sync). Having one shared codebase would simplify A LOT the development.
Why?
That's just your choice of how to build your app, right? You could've avoided this by rendering templates on the server and sending static HTML to the client, keeping the business logic on the server.
> "Most enum types are repeated"
Here's just one of ten-thousand other battle-tested options you can use: https://github.com/apache/thrift/
To take just one of many examples, so that you can do the same validation client side and the server side. You have to do the validation on the server because the client can't be trusted, but if you only do it on the server you have to do a full page load to validate any of the inputs, give feedback, or vary the form fields displayed.
e.g. how many times have you filled out a long and complicated form, pressed submit, waited several seconds, and then get dropped back at the same form where you have to hunt for the error message, change the requested field and try again. And heaven help you if you got multiple fields wrong, where the data from one informs the validation of another. Client-side logic can make this process much lower friction.
No, that's a requirement on most business cases, my comment stated 'complex and dynamic web apps'. Re-rendering the whole page everytime the user checks a box or clicks a button just to replace some fields in a whole page is (a) terrible UX, (b) hard to track the state between page refresh, (c) wrong practice and (d) bad performance.
> Here's just one of ten-thousand other battle-tested options you can use: https://github.com/apache/thrift/
Sure, I should setup a complex and huge dependency for just one of the many problems I highlighted. What a great idea
You can have the backend render partials and only send the affected part. This has been widely in use and battle-tested for about two decades in .NET WebForms, PJAX, Rails Turbolinks and other technologies.
Also, wether the app does this or that is completely orthogonal to how you build it. You don't need tho share code with the backend.
> Sure, I should setup a complex and huge dependency for just one of the many problems I highlighted. What a great idea
Except for cases where the team doesn't know anything other than JS, using this is significantly simpler than forcing the whole backend to be in JS. Also, there are several other options.
No, rendering partials is not a solution once you have a moderately complex app.
Example: The user submits a form to change an entity, you need to send a partial back for the successfully submitted form, but you also need to send partials back for potential 2-3 other places that the entity is displayed on the page, even if they are not displayed on certain pages.
Just tracking and updating them whenever they change is a pain in the ass, not to mention the increased processing/bandwidth for no reason.
Just because this is a counterexample to something that you deem good, it doesn't mean it has to be absolute shit. The world is not black and white. I would suggest at least doing some research about how things work before criticising them. They might surprise you.
Of course you shouldn't use a full-js stack if your team of developers doesn't know JS, no one is arguing that because it doesn't make sense.
This too, demonstrates the point I raised earlier.
Instead of using a mature, widely adopted and battle-tested system which allows you to efficiently share code across multiple languages without introducing runtime hits - you instead discard it as some "huge dependency" and insist on having JS everywhere, using the latest hype of the week, consequences be damned.
Why actually spend the time to solve a problem in a mature way, when I can just add this week's shiniest NPM package to imports.json?
In practice, the whole page re-render-from-server is often much faster. Compare how long loading indicators last on full-fat GMail versus Basic HTML Gmail and its full-page reloads. Fastest "web app" I've seen in the past five years was pretty complex, and it re-rendered on every action, even menu navigation. The backend? PHP. I'm in the center of the US and it was served from somewhere in Asia (Singapore, IIRC?). Still the fastest thing I've seen in a long time.
I'm pretty sure the "it's for performance" argument has been dead since we (the industry) stopped sending XML and HTML snippets for direct injection, and started sending JSON and then doing a bunch of processing on it before finally generating some DOM nodes and rendering something. In the wild, what we're doing is killing performance, not aiding it. At least two of your points are simply wrong (c, d), another is highly debatable (a), leaving only one (b) and I'm not sure that's worth the performance cost, at least in many cases.
Exactly. This way of working is such a breeze. PHP does the logic, the state is firmly in the database, and I'm from a time when peopling talking "frontend" meant HTML and CSS. Occasionally some plain JS, and I'm good.
Years ago I was out of the webdev field for some time, and I must admit that HTML and CSS alone have made big strides.
When that is the case, the stack you are describing is just perfect even nowadays.
The problem is, in most cases that is just not the case anymore.
I myself am looking to find the edges of building a web application with the "document system". Clicks and requests for a full page load don't matter that much if you're able to keep your app simple. Which is also defined by context, not only by programming skills. Certain situations or applications are just not suited for the "document system".
I spent four years working on a Meteor.js app. Meteor's main appeal is isomorphic code and a high degree of reactivity. As the app evolved we replaced the in-built MongoDB with GraphQL. It was nice to have one language and GraphQL was very useful. Eventually this kind of model may be the future, but the next project I did, instead of Meteor, I went with a more conventional Vue front end with REST & websocket api for the backend. The reason for the change is that while Node is really nice, Django and Go get predictable outcomes with less time spend on tooling, patching and updates. With a small team, losing lots of hours to "our build broke because an upstream dependency changed a function signature" is not a good thing.
Wait a minutes, so now SSR is explicit to JS and with JS CSR?
Well, kind of. The thing is that the natural progression from server side rendering/content-generation with only html and css, via Asynchronous JavaScript and XML (AJAX) to the current js-first paradigm - was a transition from hypertext document system to "movable code" (In the terms of Fielding's REST thesis)[1].
Keeping state/cache consistent works differently in the two paradigms (and they have different benefits/trade-offs).
There's a real tention between "application" and "document system". And a lot of what seems insane about contemporary web dev, is when people take something that is clearly easily/well solved as a "document system" (eg: blog/homepage) - and implement it as an application (essentially writing half of a web browser in js - with custom routing/addressing and widgets).
It's the reverse of the problem react et al tries to solve: writing an application using a document system (ie: a complex php app).
[1] Ed: In particular "Mobile Agent" - the last section in https://www.ics.uci.edu/~fielding/pubs/dissertation/net_arch...
Not really. The web implements a "document as application" or "living document" model. Since the most rudimentary software is just printing static text (and static graphics) you get quite a bit of mileage with just HTML & CSS. The web scales nicely from this to fully interactive applications, and I think some of the tension you are perceiving comes from the fact it's easy to disable or override features built into to the browse, and often developers find a way to do something novel, and in doing so, break thinks like the clipboard.
Yes, technology moves in cycles but statements like this grossly generalize.
The amount of Javascript running on the web has exploded over the last decade plus to the point that frameworks like React were released to create more modern, interactive experiences because that's what business demanded. Then the industry started figuring out that the virtual DOM was kind of a scam from a performance standpoint and started looking for ways to achieve better performance and SEO, which brought us back to SSR and more advanced client/server architectures like this.
Could you elaborate?
Not a frontend guy but my understanding was that the virtual DOM is what's necessary to enable a simplified model where you can code as if your entire view was rerendered from scratch when there's a change.
Mutating the DOM only where and when you need to in a handcrafted fashion is always going to be faster, it's just not scalable, I guess.
So, in that context the virtual DOM is not really a scam. Not sure in what sense it is? Was it claimed to be performing better than it does?
Virtual DOM never promised better performance than pre-rendered static HTML. It only achieves better performance (and better UX) than dumbly re-rendering the whole website whenever the underlying data changes.
The reason people are doing SSR with modern JS-frameworks is quite simple: some kinds of content aren't a good fit for SPAs (or maybe there are other constraints that favour rendering it all in the server), but it might still be desirable to use modern JS-frameworks. For example: you wouldn't implement a blog as an SPA, but it might still be desirable to use React to render everything.
But that was the rule, and there was no name for it. Wikipedia's SSR page used to be called "Server Side Scripting", which is kind of a misnomer, and is very generic. That would also include serving JSON from the backend... and "scripting" not only limited in scope but also non-ambiguous.
So, someone had to invent a term. The name "Server Side Rendering" is quite good actually, it describes what's actually happening rather than being some random buzzword.
(Of course, there will be people claiming "ackshually SSR is only when a Next.js-like-framework does it", but that was never agreed upon by the majority of devs)
Does this mean every single “new hotness” is truly a set change? No, of course not, a lot of things are just fads. Actually I would put SSR slightly in that camp, for while it’s certainly beneficial it’s not really a game changer. It’s just a modest performance optimisation (in some cases!).
This is easily verifiable by people claiming that the term "Server Side Rendering" can't be retroactively applied, even though it unambiguously means what PHP used to do.
I personally enjoy both the old and new techniques, and I think it's a natural progression to use those new frameworks for also rendering on the server. Not only because of code-sharing, but because I think they're better than old templating engines.
Which is a silly, lazy argument.
> by people claiming that the term "Server Side Rendering" can't be retroactively applied
Show me one such claim. The only people I've seen use this argument are the detractors. The ability for JS to run both client/server means we can do things with it that are not possible with PHP or other server-only languages, and frameworks like React are pushing that boundary. Pointing out that JS has these unique opportunities is not even remotely the same as "forgetting" and "rediscovering" SSR.
Can you provide even a single example for something we can do with JS running on both the client and the server, that was otherwise "impossible" (quoting you here), or very difficult, without this capability?
Which leads to things like... data validation logic that is exactly the same on client and server.
Same for serialization/ deserialization code.
In this thread: https://news.ycombinator.com/item?id=31723357, https://news.ycombinator.com/item?id=31723746, https://news.ycombinator.com/item?id=31724418
> The ability for JS to run both client/server means we can do things with it that are not possible with PHP or other server-only languages, and frameworks like React are pushing that boundary.
Sure it does, but this doesn't mean that PHP wasn't rendering on the server. The term still applies to what PHP was doing.
> Pointing out that JS has these unique opportunities is not even remotely the same as "forgetting" and "rediscovering" SSR.
Pointing out those unique opportunities is not a problem, and I agree with you on that. But it has absolutely nothing to do with the usage of SSR or the term SSR. Also, using the same language on server and client is older than modern JS frameworks, which definitely counts as something being "rediscovered". And yes, is definitely a cool thing, nobody is claiming the contrary.
I’ll just say it’s a bit rich to be telling the developers of React - “all this can be done with a server side rendered PHP”. The developers of React are well aware of what PHP can do, because they work at a company with one of the largest PHP (-ish) codebases in the world. Almost all pages on Facebook were completely server rendered, but they’re gradually moving towards React based web pages.
Please consider that the people making technical decisions might know what they’re doing. Please don’t condescend.
I forgot where I read it, but it was someone on the internet where they embed V8 and call from PHP (Hack).
https://hashnode.com/post/10-things-you-probably-didnt-know-...
It's true they don't really use SSR that widely, as Facebook.com doesn't really need it, but I'm pretty sure they do use streaming SSR, which offers UX benefits.
> Yes, Facebook uses SSR heavily. However, according to Lee, there are very few areas where they use React to render components on server. This was primarily a decision based on their server environment which is Hack.
Yeah this is my whole point. That person was attempting to school devs at Facebook on the benefits of server side rendering in PHP.
And whatever you said was quite niche.
My conclusion so far is that most of the criticism towards JS based solutions is largely outdated if you pick your tools and libs well. The leverage of something like Nextjs and similar is pretty significant over traditional SSR, even for cases where the latter made more sense a while ago, increasingly so. I don't think PHP is going anywhere in the near future, but I see fewer and fewer reasons to use it at all.
1. When people talk about isomorphic code for frontend related stuff, they often pick form validation as the example. But this is just one of many things. It's also the tooling, testing, types and other integrations that you miss if your frontend logic is spread across language boundaries.
2. Unoptimized performance of a PHP vs Node/Next application is quite significant as PHP needs to recreate the whole application state with every request.
3. Frontend without or "minimal" JS is a pure luxury that you almost never get to have and if you do, it comes with its own complexities.
4. Websockets and other features are a pain to use (if you can use them at all) in PHP.
5. Development is faster and more responsive with a React/Nextjs/etc. based implementation. The feedback loops are faster, tooling and libraries are more integrated.
6. There are quite few good libraries popping up in recent years for JS that give you more leverage than what I'm used to from PHP.
7. Commoditized hosting PHP has been one of its strengths, but even that is being outcompeted slowly but steadily.
8. By default there are things you cannot do or only with additional (unnecessary) effort if you split your frontend logic into two places, rather than having a single, comprehensive codebase. Frameworks and libraries like Next, Remix, this one and others are leveraging that. Yes it gets more complex but you also get more optimizations
Please note that I don't particularly like either JS or PHP. I think both languages and ecosystems are quite terrible in their own ways and have to be tamed by pragmatic developers. So no emotional attachment there.
What React introduces is functional composition to application design (if you do it right at least), and the main benefit of this is in maintainability, scalability, and reusability. For most simple apps this benefit can be completely moot, which is why some can feel the setup or learning of a new paradigm for seemingly no benefit can be a regression.
In fact if you wanted to use React in the same way that PHP was used a decade ago, you still can, and it isn’t any more difficult to do so. You just end up with much less maintainable code. You can render React server side just like PHP, and either mount dynamic client side interactive components purely on the client side just like the “good old days,” or you can also render them server side and take advantage of client side hydration at the component level (like the previous method but you also get an initial server render). While this sounds complex, it really is just about as complex as making a server rendered PHP with some interactive JS bits, but just adding some new words to describe the process.
The goal of new React features like server components is to bring the maintainability of functional composition and get this optimization for free while still being able to define your UI in terms of reusable functions.
So, even if the page has a single React component, does it ship the entire react + react-dom bundle?
Island-based probably means that hydration is not whole-page but granular, making parts unshade at different times, to your enjoyment.
Cynicism detected!
We're just using newer client-side frameworks to also render things on the server, because for lots of people there is a clear advantage in using such frameworks rather than old templating engines. Advantages often include more ergonomic APIs and code-sharing.
No that _is_ the point.
I think this is great.
I didn't hate jQuery. I just hated the context switch between regular HTML templates, and creating a jQuery component. If the entire frontend can be treated uniformly, that's a huge plus.
Likewise, ingress/egress is not quite the same as read/write. It gets its own terminology because it's distinct.
I see zero difference with entity/network read/write rate.
As for hydration - sounds highly inappropriate to me but whatever warms the cockles of their hearts.
Ingress/egress are also important in networking because they communicate direction -- lots of pipes are asymmetric, and a firewall allowing all egress is very different from allowing all ingress.
Ingress/egress also measure total data, when the gadget might only "read" a fraction of the traffic. For example, if a hardware-accelerated router makes decisions based on just a few fields of an IP packet, did it "read" the whole packet?
Is there a reason someone like me should care? Does it improve anything by atleast an order of magnitude?
> Zero runtime overhead: no JS is shipped to the client by default.
Translation: the UX most people on HN complaining about JS want. It’s just the interactive parts, none of the treating a web page like it’s an app, but devs familiar with developing sites that way can use it that way without jamming MBs of JS down users’ browsers.
Microsoft alone has made more proprietary native app frameworks for Windows in the last 15 than hipster Javascript developers had to learn new frameworks for work.