Render on the Server. Until You Can't.
One of the core principles of Pinstripe (my full-stack JavaScript web framework) is that it's server first. This means rendering HTML anywhere other than the server should be the exception to the rule.
As developers, I strongly believe we should always try to make things as simple as possible, but not simpler. We should strive to remove as much as we can from a solution while still having it work.
I believe a good heuristic for achieving this is to render everything you can on the server — until you can't. In other words, do everything on the server until it starts to hurt the UX (user experience).
Let me explain why I think the classical approach is underrated.
Why the classical approach is underrated
Whenever you want to render something on the page, all web apps — whether they're back-end or front-end orientated — roughly follow the same steps:
- Receive some params from the user.
- Collect the required data based on those params.
- Output a response based on the collected data.
For most apps to be useful, the data needs to be pooled somewhere so it can be easily shared and processed. For a lot of apps (Blognami included) this usually takes the form of a database.
The classical approach
For classical server-side apps this is trivially easy — you run some SQL and you're done. The cost of talking directly to a database is really cheap; the round trip is minimal.
Outputting HTML on the server side also means the data you expose is the minimum needed for the use case at hand.
And out of the box, these apps can be easily read by search bots, making them SEO friendly with little effort.
The modern approach
Contrast this with client-side rendering. The data needs to be collected via some sort of API, which is typically a lot slower than a direct database connection — and that's compounded if multiple trips need to be made.
The other issue with an API is that it often exposes more data than you need. This extra baggage both slows down the response time and can pose security risks by exposing data that might be maliciously taken advantage of.
And to make these apps SEO friendly, you'll probably need to implement server-side rendering anyway — which requires its own specialised server.
Despite the extra plumbing, the modern approach does have some distinct advantages:
- Once the data has loaded into the client, the actual rendering can happen really fast.
- It can present intermediate states as the data loads in. For example, you can show skeleton pages or loading indicators, keeping the end user psychologically at ease.
Pinstripe's approach
Pinstripe goes a long way to ensure the bulk of the HTML is rendered on the server side, because this makes development so much simpler.
But it still retains most of what's good about client-side frameworks, at a fraction of the complexity. It uses a few tricks to achieve this.
Soft page refreshes
When a user clicks a link or submits a form, a request is made to the server. Normally a browser would do a full page refresh — Pinstripe doesn't. Instead it rewires the default behaviour so that when the HTML comes back, it's used to surgically update only the parts of the DOM that have changed, without a costly full refresh.
This effectively turns Pinstripe into a SPA (single-page app) framework — where the bulk of the HTML is still rendered on the server side.
Use of placeholders
Links and forms have been extended so you can specify a placeholder, via a URL, that's shown while the browser waits for a response from the server.
These placeholders are eagerly loaded into cache so the UI feels very responsive when the user clicks a link or submits a form.
A placeholder can be made up of arbitrary HTML, but will typically be composed of skeleton components or a loading indicator of some description.
Use of caching
If Pinstripe has a version of a GET request in cache, it loads that version first while it fetches the newer one. The user gets an instant response, followed by an update if anything has actually changed.
You can also tell links to eagerly load requests into cache.
Client-side rendering
Sometimes server-side rendering isn't good enough. In this rare case, you can enable client-side rendering — via a single-line change — for specific endpoints.
When you do, those endpoints get added to a service worker that runs inside the browser.
Blognami uses this when rendering the preview pane in the markdown editor.
Conclusion
I hope this inspires you to take a look at Pinstripe / Blognami!
If you have any questions, please reach out to me.