Rendering on the Web

<strong>Should I use SSR? What about Rehydration? Where should we implement logic and rendering in our applications?</strong>

Should I use SSR? What about Rehydration? Where should we implement logic and rendering in our applications?

As developers, we are often faced with decisions that will affect the entire architecture of our applications. One of the core decisions web developers must make is where to implement logic and rendering in their application. This can be a difficult, since there are a number of different ways to build a website.

Our understanding of this space is informed by our work in Chrome talking to large sites over the past few years. Broadly speaking, we would encourage developers to consider server rendering or static rendering over a full rehydration approach.

In order to better understand the architectures we’re choosing from when we make this decision, we need to have a solid understanding of each approach and consistent terminology to use when speaking about them. The differences between these approaches help illustrate the trade-offs of rendering on the web through the lens of performance.


Terminology

Rendering

  • SSR: Server-Side Rendering - rendering a client-side or universal app to HTML on the server.
  • CSR: Client-Side Rendering - rendering an app in a browser, generally using the DOM.
  • Rehydration: “booting up” JavaScript views on the client such that they reuse the server-rendered HTML’s DOM tree and data.
  • Prerendering: running a client-side application at build time to capture its initial state as static HTML.

Performance

  • TTFB: Time to First Byte - seen as the time between clicking a link and the first bit of content coming in.
  • FP: First Paint - the first time any pixel gets becomes visible to the user.
  • FCP: First Contentful Paint - the time when requested content (article body, etc).
  • TTI: Time To Interactive - the time at which a page becomes interactive (events wired up, etc).
Server Rendering

Server rendering generates the full HTML for a page on the server in response to navigation. This avoids additional round-trips for data fetching and templating on the client, since it’s handled before the browser gets a response.

Server rendering generally produces a fast First Paint (FP) and First Contentful Paint (FCP). Running page logic and rendering on the server makes it possible to avoid sending lots of JavaScript to the client, which helps achieve a fast Time to Interactive (TTI). This makes sense, since with server rendering you’re really just sending text and links to the user’s browser. This approach can work well for a large spectrum of device and network conditions, and opens up interesting browser optimizations like streaming document parsing.

With server rendering, users are unlikely to be left waiting for CPU-bound JavaScript to process before they can use your site. Even when third-party JS can’t be avoided, using server rendering to reduce your own first-party JS costs can give you more “budget” for the rest. However, there is one primary drawback to this approach: generating pages on the server takes time, which can often result in a slower Time to First Byte (TTFB).

Whether server rendering is enough for your application largely depends on what type of experience you are building. There is a longstanding debate over the correct applications of server rendering versus client-side rendering, but it’s important to remember that you can opt to use server rendering for some pages and not others. Some sites have adopted hybrid rendering techniques with success. Netflix server-renders its relatively static landing pages, while prefetching the JS for interaction-heavy pages, giving these heavier client-rendered pages a better chance of loading quickly.

Many modern frameworks, libraries and architectures make it possible to render the same application on both the client and the server. These techniques can be used for Server Rendering, however it’s important to note that architectures where rendering happens both on the server and on the client are their own class of solution with very different performance characteristics and tradeoffs. React users can use renderToString() or solutions built atop it like Next.js for server rendering. Vue users can look at Vue’s server rendering guide or Nuxt. Angular has Universal. Most popular solutions employ some form of hydration though, so be aware of the approach in use before selecting a tool.


Static Rendering

Static rendering happens at build-time and offers a fast First Paint, First Contentful Paint and Time To Interactive - assuming the amount of client-side JS is limited. Unlike Server Rendering, it also manages to achieve a consistently fast Time To First Byte, since the HTML for a page doesn’t have to be generated on the fly. Generally, static rendering means producing a separate HTML file for each URL ahead of time. With HTML responses being generated in advance, static renders can be deployed to multiple CDNs to take advantage of edge-caching.

Solutions for static rendering come in all shapes and sizes. Tools like Gatsby are designed to make developers feel like their application is being rendered dynamically rather than generated as a build step. Others like Jekyl and Metalsmith embrace their static nature, providing a more template-driven approach.

One of the downsides to static rendering is that individual HTML files must be generated for every possible URL. This can be challenging or even infeasible when you can’t predict what those URLs will be ahead of time, or for sites with a large number of unique pages.

React users may be familiar with Gatsby, Next.js static export or Navi - all of these make it convenient to author using components. However, it’s important to understand the difference between static rendering and prerendering: static rendered pages are interactive without the need to execute much client-side JS, whereas prerendering improves the First Paint or First Contentful Paint of a Single Page Application that must be booted on the client in order for pages to be truly interactive.

If you’re unsure whether a given solution is static rendering or prerendering, try this test: disable JavaScript and load the created web pages. For statically rendered pages, most of the functionality will still exist without JavaScript enabled. For prerendered pages, there may still be some basic functionality like links, but most of the page will be inert.

Another useful test is to slow your network down using Chrome DevTools, and observe how much JavaScript has been downloaded before a page becomes interactive. Prerendering generally requires more JavaScript to get interactive, and that JavaScript tends to be more complex than the Progressive Enhancement approach used by static rendering.


Server Rendering vs Static Rendering

Server rendering is not a silver bullet - its dynamic nature can come with significant compute overhead costs. Many server rendering solutions don’t flush early, can delay TTFB or double the data being sent (e.g. inlined state used by JS on the client). In React, renderToString() can be slow as it’s synchronous and single-threaded. Getting server rendering “right” can involve finding or building a solution for component caching, managing memory consumption, applying memoization techniques, and many other concerns. You’re generally processing/rebuilding the same application multiple times - once on the client and once in the server. Just because server rendering can make something show up sooner doesn’t suddenly mean you have less work to do.

Server rendering produces HTML on-demand for each URL but can be slower than just serving static rendered content. If you can put in the additional leg-work, server rendering + HTML caching can massively reduce server render time. The upside to server rendering is the ability to pull more “live” data and respond to a more complete set of requests than is possible with static rendering. Pages requiring personalization are a concrete example of the type of request that would not work well with static rendering.

Server rendering can also present interesting decisions when building a PWA. Is it better to use full-page service worker caching, or just server-render individual pieces of content?


Client-Side Rendering (CSR)

Client-side rendering (CSR) means rendering pages directly in the browser using JavaScript. All logic, data fetching, templating and routing are handled on the client rather than the server.

Client-side rendering can be difficult to get and keep fast for mobile. It can approach the performance of pure server-rendering if doing minimal work, keeping a tight JavaScript budget and delivering value in as few RTTs as possible. Critical scripts and data can be delivered sooner using HTTP/2 Server Push or <link rel=preload>, which gets the parser working for you sooner. Patterns like PRPL are worth evaluating in order to ensure initial and subsequent navigations feel instant.

The primary downside to Client-Side Rendering is that the amount of JavaScript required tends to grow as an application grows. This becomes especially difficult with the addition of new JavaScript libraries, polyfills and third-party code, which compete for processing power and must often be processed before a page’s content can be rendered. Experiences built with CSR that rely on large JavaScript bundles should consider aggressive code-splitting, and be sure to lazy-load JavaScript - “serve only what you need, when you need it”. For experiences with little or no interactivity, server rendering can represent a more scalable solution to these issues.

For folks building a Single Page Application, identifying core parts of the User Interface shared by most pages means you can apply the Application Shell caching technique. Combined with service workers, this can dramatically improve perceived performance on repeat visits.


Combining server rendering and CSR via rehydration

Often referred to as Universal Rendering or simply “SSR”, this approach attempts to smooth over the trade-offs between Client-Side Rendering and Server Rendering by doing both. Navigation requests like full page loads or reloads are handled by a server that renders the application to HTML, then the JavaScript and data used for rendering is embedded into the resulting document. When implemented carefully, this achieves a fast First Contentful Paint just like Server Rendering, then “picks up” by rendering again on the client using a technique called (re)hydration. This is a novel solution, but it can have some considerable performance drawbacks.

The primary downside of SSR with rehydration is that it can have a significant negative impact on Time To Interactive, even if it improves First Paint. SSR’d pages often look deceptively loaded and interactive, but can’t actually respond to input until the client-side JS is executed and event handlers have been attached. This can take seconds or even minutes on mobile.

Perhaps you’ve experienced this yourself - for a period of time after it looks like a page has loaded, clicking or tapping does nothing. This quickly becoming frustrating… “Why is nothing happening? Why can’t I scroll?”


A Rehydration Problem: One App for the Price of Two

Rehydration issues can often be worse than delayed interactivity due to JS. In order for the client-side JavaScript to be able to accurately “pick up” where the server left off without having to re-request all of the data the server used to render its HTML, current SSR solutions generally serialize the response from a UI’s data dependencies into the document as script tags. The resulting HTML document contains a high level of duplication:

As you can see, the server is returning a description of the application’s UI in response to a navigation request, but it’s also returning the source data used to compose that UI, and a complete copy of the UI’s implementation which then boots up on the client. Only after bundle.js has finished loading and executing does this UI become interactive.

Performance metrics collected from real websites using SSR rehydration indicate its use should be heavily discouraged. Ultimately, the reason comes down to User Experience: it’s extremely easy to end up leaving users in an “uncanny valley”.

There’s hope for SSR with rehydration, though. In the short term, only using SSR for highly cacheable content can reduce the TTFB delay, producing similar results to prerendering. Rehydrating incrementally, progressively, or partially may be the key to making this technique more viable in the future.


Streaming server rendering and Progressive Rehydration

Server rendering has had a number of developments over the last few years.

Streaming server rendering allows you to send HTML in chunks that the browser can progressively render as it’s received. This can provide a fast First Paint and First Contentful Paint as markup arrives to users faster. In React, streams being asynchronous in renderToNodeStream() - compared to synchronous renderToString - means backpressure is handled well.

Progressive rehydration is also worth keeping an eye on, and something React has been exploring. With this approach, individual pieces of a server-rendered application are “booted up” over time, rather than the current common approach of initializing the entire application at once. This can help reduce the amount of JavaScript required to make pages interactive, since client-side upgrading of low priority parts of the page can be deferred to prevent blocking the main thread. It can also help avoid one of the most common SSR Rehydration pitfalls, where a server-rendered DOM tree gets destroyed and then immediately rebuilt - most often because the initial synchronous client-side render required data that wasn’t quite ready, perhaps awaiting Promise resolution.


Partial Rehydration

Partial rehydration has proven difficult to implement. This approach is an extension of the idea of progressive rehydration, where the individual pieces (components / views / trees) to be progressively rehydrated are analyzed and those with little interactivity or no reactivity are identified. For each of these mostly-static parts, the corresponding JavaScript code is then transformed into inert references and decorative functionality, reducing their client-side footprint to near-zero.

The partial hydration approach comes with its own issues and compromises. It poses some interesting challenges for caching, and client-side navigation means we can’t assume server-rendered HTML for inert parts of the application will be unavailable.


Trisomorphic Rendering

If service workers are an option for you, “trisomorphic” rendering may also be of interest. It’s a technique where you can use streaming server rendering for initial/non-JS navigations, and then have your service worker take on rendering of HTML for navigations after it has been installed. This can keep cached components and templates up to date and enables SPA-style navigations for rendering new views in the same session. This approach works best when you can share the same templating and routing code between the server, client page, and server worker.


Wrapping up…

When deciding on an approach to rendering, measure and understand what your bottlenecks are. Consider whether static rendering or server rendering can get you 90% of the way there. It’s perfectly okay to mostly ship HTML with minimal JS to get an experience interactive. Here’s a handy infographic showing the server-client spectrum:


Credits

Thanks to everyone for their reviews and inspiration:

Jeffrey Posnick, Houssein Djirdeh, Shubhie Panicker, Chris Harrelson, and Sebastian Markbåge

Single Page Application with Angular.js Node.js and CouchDB

Single Page Application with Angular.js Node.js and CouchDB

We posted the article on Single Page Application with Angular.js, Node.js and CouchDB with Express

We posted the article on Single Page Application with Angular.js, Node.js and CouchDB Cradle Module with Express 3. Current post is a way to migrate the same with Express 4.

This article describes a way to make a web application with Javascript based Web Server with CouchDB with Angular.js and Node.js.

Here we have selected

  • Angular.js for client side development – Single Page Application
  • Cross Domain Communication in between Angular.js and Node.js
  • Node.js for server side development
  • Rest based web service creation with express.js (Express 4)
  • Database – CouchDb
  • Node.js Cradle Module Extention (to make communication with CouchDB)
  • Making CouchDB views with Map-Reduce to fetch the data

We have created a Proof of Concept with Javascript based web server, where we have focused on dealing with NoSql (CouchDB) with javascript based framework Node.js and angular.js on client side.

Architecture at a glance



Steps

Installation

  • Download and install Node.js from here.
  • To Develop the application we need to install cradle module for Node.js
  • Command – **npm install cradle **(should be connected to internet)
  • We need to install express.js for node.jsCommand – npm install express (should be connected to internet)

Configuration Code

Now we will try to describe the code portion

var application_root = __dirname,
express = require("express"),
path = require("path");

Here we have initialised the express.js within javascript variables in respect of Node.js concept.

var databaseUrl = "sampledb";
var cradle = require('cradle');
var connection = new(cradle.Connection)('http://admin:[email protected]', 5984, {
auth: { username: 'admin', password: 'admin' }
});
var db = connection.database(databaseUrl);

Here we have initialised the express web server in app variable.

var databaseUrl = "sampledb";
var cradle = require('cradle');
var connection = new(cradle.Connection)('http://admin:[email protected]', 5984, {
auth: { username: 'admin', password: 'admin' }
});
var db = connection.database(databaseUrl);

Here we have made the connection to the couchdb database using the Node.js cradle module extension library.

// Config
var env = process.env.NODE_ENV || 'development';
if ('development' == env) {
var bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
var methodOverride = require('method-override');
app.use(methodOverride());
app.use(express.static(path.join(application_root, "public")));
var errorhandler = require('errorhandler');
app.use(errorhandler());
}

Here we have made the configuration related to express.js which is very much different from Express 3.

In Express 3 the same portion was written as

      app.configure(function () {
         app.use(express.bodyParser());
         app.use(express.methodOverride());
         app.use(app.router);
         app.use(express.static(path.join(application_root, "public")));
         app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
      });

This makes a little sense how the Express 3 is migrated to Express 4.

In Express 4 app.use(app.router); is removed. Instead it is added with the Rest services.The way of configuration is also changed as well as the changes reflected in use of bodyParser() ,methodOverride() ,methodOverride() ,errorHandler({…}).

Rest Services Code

app.route('/api').get(function (req, res) {
   res.header("Access-Control-Allow-Origin", "http://localhost");
   res.send('Express API is running');
});

Here we have made our first REST based web service and tested whether the express.js is running.Our sample api will be http://127.0.0.1:1212/api or http://localhost:1212/api

app.route('/getangularusers').get(function (req, res) {
 res.header("Access-Control-Allow-Origin", "http://localhost");
 res.header("Access-Control-Allow-Methods", "GET, POST");
 res.writeHead(200, {'Content-Type': 'application/json'});
 str='[';
 db.view('characters/all', function (err, response) {
    response.forEach(function (row) {
            //console.log("%s is on the %s side of the force.", row.name, row.force);
        str = str + '{ "name" : "' + row.username + '"},' +'\n';
        });
        str = str.trim();
        str = str.substring(0,str.length-1);
    str = str + ']';
    res.end( str);
 });
});

Here we have created another REST api to get all username from user collection and so we have done the cradle query.


CouchDB View Creation

This view creation is to be executed, before running the Angular Client Application to get specific set of data from couchdb.

To run this view creation, we should put

[httpd]  
authentication_handlers = {couch_httpd_auth, null_authentication_handler}  

in the local.ini file of <>/etc/couchdb folder to execute this map function without being the admin user.

So the REST api of view creation is

app.route('/createview').get(function (req, res) { // Before running the Angular Application, the view must be created
 db.save('_design/characters', { // Main View Family Name
        all: { // A particular set of data selection by javascript map-reduce
        map: function (doc) {
           if (doc.username) emit(doc.username, doc); // specific code to execute map function on each document
       }
     }
   });
});

and the sample api will be – http://127.0.0.1:1212/getangularusers (Get Method).

Next we have made a POST request to create an user via REST calling.

app.route('/insertangularcouchuser').post(function (req, res){
console.log("POST: ");
res.header("Access-Control-Allow-Origin", "http://localhost");
res.header("Access-Control-Allow-Methods", "GET, POST");
// The above 2 lines are required for Cross Domain Communication(Allowing the methods that come as Cross
// Domain Request
console.log(req.body);
console.log(req.body.mydata);
var jsonData = JSON.parse(req.body.mydata);
var doc = {email: jsonData.email, password: jsonData.password, username: jsonData.username};
db.save('\''+Math.random()+'\'', doc, function (err, res) {
   if (err) {
       // Handle error
       res += ' SAVE ERROR: Could not save record!!\n';
       } else {
       // Handle success
       res += ' SUCESSFUL SAVE\n';
       }
   });
});

Our sample api will be – http://127.0.0.1:1212/insertangularcouchuser (Post Method – accessed by client side)

// Launch server
app.listen(1212);

We have made the server to listen at 1212 port.

Now run node appcouchdbangular.js from command shell. This is our node.js specific code file.

For references :


Angular.js part

Below is the code in Angular Controller

'use strict';

var myApp = angular.module('myApp', []); // <strong>Taking Angular Application in Javascript Variable</strong>

// <strong>Below is the code to allow cross domain request from web server through angular.js</strong>
myApp.config(['$httpProvider', function($httpProvider) {
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];
}
]);

/* Controllers */

function UserListCtrl($scope, $http, $templateCache) {

var method = 'POST';
var inserturl = 'http://localhost:1212/insertangularcouchuser';// <strong>URL where the Node.js server is running</strong>
$scope.codeStatus = "";
$scope.save = function() {
//<strong> Preparing the Json Data from the Angular Model to send in the Server.</strong>
var formData = {
'username' : this.username,
'password' : this.password,
'email' : this.email
};

this.username = '';
this.password = '';
this.email = '';

var jdata = 'mydata='+JSON.stringify(formData); // &lt;strong&gt;The data is to be string.&lt;/strong&gt;

$http({ &lt;strong&gt;// Accessing the Angular $http Service to send data via REST Communication to Node Server.&lt;/strong&gt;
        method: method,
        url: inserturl,
        data:  jdata ,
        headers: {'Content-Type': 'application/x-www-form-urlencoded'},
        cache: $templateCache
    }).
    success(function(response) {
    console.log("success"); &lt;strong&gt;// Getting Success Response in Callback&lt;/strong&gt;
            $scope.codeStatus = response.data;
    console.log($scope.codeStatus);

    }).
    error(function(response) {
    console.log("error"); &lt;strong&gt;// Getting Error Response in Callback&lt;/strong&gt;
            $scope.codeStatus = response || "Request failed";
    console.log($scope.codeStatus);
    });
$scope.list();&lt;strong&gt;// Calling the list function in Angular Controller to show all current data in HTML&lt;/strong&gt;
    return false;

};

$scope.list = function() {
var url = 'http://localhost:1212/getangularusers';// <strong>URL where the Node.js server is running</strong>
$http.get(url).success(function(data) {
$scope.users = data;
});
<strong>// Accessing the Angular $http Service to get data via REST Communication from Node Server </strong>
};

$scope.list();
}

Angular Template and HTML

       <html lang="en" ng-app="myApp">
.....

We have referred the Angular Application in above code

   <body ng-controller="UserListCtrl">
.....

We have referred the Angular Controller in above code

   Search: <input ng-model="user">
<div class="span10">
<!--Body content-->
<ul class="users">
<li ng-repeat="user in users | filter:user ">
{{user.name}}
</li>
</ul>
</div>

We have used the ng-repeat tag to take the users data model from REST communication and shown in HTML

   <form name="myform" id="myform1" ng-submit="save()">
<fieldset>
<legend>New User</legend>
<div class="control-group">
<center>
<input type="text" placeholder="User…" ng-model="username" size=50 required/>
</center>
<center>
<input type="text" placeholder="Password…" ng-model="password" size=50 required/>
</center>
<center>
<input type="text" placeholder="Email…" ng-model="email" size=50 required/>
</center>
</div>
</fieldset>
<p>
<div><center><button type="submit" >Save now...</button></center></div>
</p>
</form>

We have used the ng-submit tag to send the user data model from REST communication and sent to node server to save in CouchDB.

Reader can download the complete source-code in GitHub.

Thanks for reading

If you liked this post, share it with all of your programming buddies!

Follow us on Facebook | Twitter

Learn More

The Complete Node.js Developer Course (3rd Edition)

Angular & NodeJS - The MEAN Stack Guide

NodeJS - The Complete Guide (incl. MVC, REST APIs, GraphQL)

Node.js: The Complete Guide to Build RESTful APIs (2018)

Angular 7 (formerly Angular 2) - The Complete Guide

Angular & NodeJS - The MEAN Stack Guide

The Web Developer Bootcamp

MEAN Stack Tutorial MongoDB, ExpressJS, AngularJS and NodeJS

Angular 7 CRUD With Node.JS API

Angular and Nodejs Integration Tutorial

Angular + WebSocket + Node.js Express = RxJS WebSocketSubject ❤️

Setting up your new Mac for MEAN Stack development

Front-end Developer Handbook 2019

Originally published on https://codequs.com

Hetu Rajgor's answer to What are the major differences between Java, AngularJS, and JavaScript and Node JS? - Quora

<img src="https://moriohcdn.b-cdn.net/70b437cf37.png">Java is a programming language which is owned by Oracle. More than 3 Million devices are running in Java.&nbsp;JS is a client-side programming language used for creating dynamic websites and apps to run in the client's browser.

Java is a programming language which is owned by Oracle. More than 3 Million devices are running in Java. JS is a client-side programming language used for creating dynamic websites and apps to run in the client's browser.

Node vs Angular : Comparing Two Strong JavaScript Technologies

Just from being a simple client-side scripting language, JavaScript has evolved over the years to turn out to be a powerful programming language. Here Node.js is a cross-platform runtime environment while AngularJS is one of the top JavaScript framework. Angular helps the developers to build web applications which are dynamic in nature using HTML template language and following the MVC design pattern.