Back in the old days, web pages were served as is by static servers. All navigation and interaction were based on those pages; you had to transition from one page to another, nothing was dynamic, and you could not show or hide a block of text, or do something similar.
Later, servers started to use templates to deliver HTML to the client. Languages such as Java, PHP, Python, ASP, VBScript, Perl, and Ruby were suitable for such tasks. Those pages were called server-generated. The interaction though was still the same: page transitions.
Later on, due to the shift towards REST APIs, a cleaner separation of concerns brought the industry away from server-generated approaches to fully JS-driven apps, which were capable of rendering the pure data into HTML by using client-side templates.
But in order to more efficiently load the initial data for a JS app, we can utilize the server-generated approach a little bit. We can render the initial markup on the server and then let the JS app take it over later. The main assumption here is the fact that the server-side renderer is usually much closer to the API server, ideally in the same data center, and thus it has much better connection and way more bandwidth than remote clients (browsers in our case). It also can utilize all benefits of HTTP2 or any other protocol to maintain fast data exchange.
The server can optimize rendering time by caching the results from rendering individual components or entire pages, indexed on a serialization of the parameters that affect the output - that are used by the component. This is particularly true of components or entire pages that will be viewed repeatedly in the same state, either just by one user or potentially by many different users. Where appropriate, this strategy may even allow the server-side renderer to reduce load on the API server, if cached component renders are given a TTL.
The server-side renderer is capable of doing all those chained requests much faster than clients, and all the codebase can be pre-loaded and pre-parsed. It can also use more aggressive data caching strategies, since invalidation can also be centrally maintained.
To decrease code duplication, we would like to use the same technology and the same templates both on the client and on the server. Such an app is called universal or isomorphic.
The general approach is as follows: we take the Node.js server, install a web framework, and start listening to incoming requests. On every request that matches a certain URL, we take the client scripts and use them to bootstrap the initial state of the app for the given page. Then, we serialize the resulting HTML and data, bake it together, and send it to the client.
The client immediately shows the markup and then bootstraps the app on the client, applying initial data and state, and hence taking control.
The next page transition will happen completely on the client; it will load data from regular API endpoints just like before. One of the trickiest parts of this approach is to make sure that the same page with the same HTML will be rendered both on the client and on the server, which means we need to make sure the client app will be bootstrapped in a certain state that will result in the same HTML.
This brings us to the choice of framework. Not all client-side frameworks are capable of server-side rendering; for instance, it would be quite challenging to write a jQuery app that will pick up state and render itself correctly on top of existing HTML.