CSR, SPA, SSR, SSG and the JAMstack

How do modern web applications work? Join me as I demystify the workings of contemporary web application architectures and review the pros and cons of each.

Post last updated: April 25, 2021

CSR, SPA, SSR, SSG and the JAMstack - What does it all mean? Skimming through the definitions of these acronyms, used to describe modern web applications, many of them sound confoundingly similar so I wanted to take the time in this article to deconstruct them in more detail.

Server Side Rendering

Server side rendering process for web applications

If we go back in time to the formative years of the 21st century, to the time before before single-page apps, pages in web applications were generated entirely on the server. There would be a back-end framework, such as PHP or ASP or Java. Let's take a look a how this process works.

  • A user opens their browser and enter a URL.
  • An HTTP request travels from the user's browser to a web server.
  • The web server receives the request, hands if off to the back-end framework which processes it and generate the HTML content of the page.
  • The web server returns the generated HTML in the HTTP response.
  • The user's browser receives the response, renders the HTML and the user sees a web page.

Every time the user did something else on the website, navigated to a new page, clicked a button or whatever the process would start all over again. JavaScript could be used with moderation to add extra interactivity and dynamism to a website but was generally avoided. To put a label on this, we'll call it server-side rendering, but we'll come back to this a bit later.

Single Page Applications

Now we skip forward a few years, the XmlHttpRequest API becomes more readily available, AJAX becomes a thing that developers are interested in and soon single page web applications start to appear.

Single page applications improved the experience for users by eliminating the annoying page reload cycle whenever the current page needed to be updated or whenever the website needed to request the server, the back-end, to perform an operation.

A frequently used acronym to refer to the process by which a single page application works is CSR, Client Side Rendering. In a single page application there is a single entry page for the application. In this model, the user navigates to the index.html (just as an example, app.html is also common) page of the website. The server returns a response representing an HTML page along with links to JavaScript files. The browser downloads the JavaScript files and executes them. This causes the single page application to initialise and once complete the user is able to see and interact with the application.

Client Side Rendering process for web applications

Take for example the following basic example in Angular.js:

<html ng-app="myApp">
<head>
    <title>My Application</title>
    <script src="lib/angular/angular.js"></script>
    <script src="app.js"></script>
</head>
<body>
</body>
</html>

The first script tag loads and initializes the Angular.js framework. The second script tag loads the AngularJS application.

Angular.js used an MVC architecture where:

  • The views are represented by HTML files.
  • The controllers are represented by JavaScript files.
  • The model, in any non-trivial application, would be implemented primarily in the back-end. Controllers would interact with a REST API, performing operations, storing and retrieving data.

Let's review the pros and cons of single page applications:

Advantages

  • Speed: The elimination of the need to constantly reload the entire page improved the performance as there is less work being offloaded to the web server and more work being done locally in the user's browser.
  • User Experience: The user experience improved as a result of being able to make the page more dynamic and more interactive.
  • Caching: The ability to cache data locally meant that there was a reduced need to make roundtrips to the server to load data.

Disadvantages

  • JavaScript: The dependency on JavaScript means that the application will not work if JavaScript is disabled on the browser. This is less of a problem now as JavaScript has become more accepted and more integral to the web.
  • Security: SPAs are potentially more vulnerable to XSS attacks. SPA frameworks do include measures to mitigate this problem but if the developers are not aware of the potential issues and how to develop for them there could be issues.
  • SEO: One of the issues with SPAs has been SEO. For a search-engine crawler to be able to effectively crawl a SPA it actually has to render the application and while crawlers have become better at handling JavaScript it still remains a challenge.

Crawler technology has improved somewhat. While older versions of GoogleBot experienced many difficulties with single-page applications and JavaScript in general, GoogleBot now uses a current version of Google Chrome to crawl web pages. I understand that Microsoft's Bing also uses a similar approach. Of course, all this extra complexity involved in crawling means that it becomes more expensive for Google as crawling now requires so much extra work.

Some of the early popular SPA frameworks included Backbone.js, Knockout.js, Ember.js and Angular.js. Whilst these frameworks are still around, mainly in legacy applications, they have effectively been superseded by a new breed of frameworks that take advantages of improvements in JavaScript and other web technologies. Currently popular SPA frameworks include Angular, React and Vue.

Static Site Generators (SSGs)

Static Site Generators consume a collection of content, for instance a set of Markdown files, and transform that content using a rendering engine. The output from the process is a collection of HTML files representing the generated website. This can then be pushed out to a web server.

Static site generators turn assets into websites

Use cases for statically generated sites include online documentation, manuals for products and applications, blogs, brochures and certain kinds of reporting. Let's say I've built an application or designed a new product but it needs a manual to fully explain all the capabilities of it. I could write a manual, using markdown. My manual might consist of numerous pages of content explaining different facets of my product and could contain images, diagrams and so on. I feed all that content into the static site generator and it generates a website, a set of HTML pages along with all the images. I can tell the SSG to use a particular theme, probably one that matches my brand. Then I deploy the site onto my web server and I'm done.

If I need to make some revisions or additions I edit the markdown files, process the content through the SSG again and redeploy. If I found I was doing this regularly I would probably want to automate the process so that I edited the markdown files, then checked them into a source control system. This could then trigger an automated build and deployment. But this additional automation really goes beyond the core idea of SSG.

11ty

Let's take a look at using some of these static site generators. To start with I'll use the site generator 11ty. First, I create a new directory for my project, named example. Then I use npm to create a package.json file:

mkdir example
cd example
npm init -y

Next, I install eleventy into the package.json:

npm install --save-dev @11ty/eleventy

Then I create a doc subdirectory within the example folder. This will be where the source documents for my website reside.

cd example
mkdir doc 

Now, I'll create a simple Markdown document, index.md, inside the doc folder.

Simple Markdown document

The content between the --- markers is defined as front matter (from YAML) and is used to provide metadata for the page which will be used by eleventy and any templates.

Now I create a configuration file .eleventy.js and place this in the project root directory. This file tells eleventy where the source documents are and where to place the generated output.

module.exports = {
    dir: {
      input: 'doc',
      output: 'dist'
    }
};

Finally, I run eleventy again which will build the website and startup a hot-reloading server using browsersync.

npx @11ty/eleventy --serve

The result is a simple website with one file, index.html, containing the rendered content from the markdown file index.md.

As an aside, one thing to note with eleventy is that it calls itself a simple SSG, which is true enough to begin with but I found it quickly becomes challenging once you get beyond the basics and the documentation can be difficult to follow at times.

Let's review the pros and cons of SSGs:

Advantages

  • Simplicity: Building and deploying content using an SSG is uncomplicated compared to almost all other types of websites.
  • Speed: SSG sites can be highly performant as the web server is sending pre-generated content.
  • Security: Because there are no databases or integration with other systems the security risk is vastly reduced.

Disadvantages

  • User Experience: Unsuitable for dynamic websites where a high degree of interactivity is required. Some SSGs are better than others in this regard but still, for pure SSGs at least, this isn't their strong point.
  • Content: The content on the website has the potential to become out of date unless the site is redeployed regularly. There are solutions to this but this characteristic makes it less suitable for more dynamic, rapidly changing content.

There is a lot of variance between the capabilities of the various SSG offerings. At one end there are tools like 11ty and others that transform the content from an input format into an output format (usually HTML), generating a website. Some are more sophisticated than others, in terms of supporting plugins, themes, templates and so on. Then there are frameworks like Nuxt.js and Gatsby that claim SSG capabilities as they allow developers to leverage the power of a Vue and React to build complete dynamic applications. In reality, whilst there is an element of static site generation, these frameworks are capable of building complex dynamic web applications and cross the line into JAMstack and the modern take on SSRs, Universal Apps, which we will look at next.

JAMstack

The JAM in JAMstack stands for JavaScript, API and Markup.

Applications built using a JAMstack architecture are pre-generated or pre-rendered during the build stage. The generated output from the build is then deployed to a server, which could be a simple static hosting site or a CDN. JAMstack defines a simple architecture that includes a static site generator; in fact a static site generator is a key tool in the process. The defining characteristics of JAMstack are:

  • Pre-generated front-end: The website is a static website, consisting of the HTML files plus any linked assets such as images, JavaScript etc. Because it is static, with no dependency on back-end server, this allows the website to be deployed to a CDN, providing high-availability and high-performance.
  • Enhancement with JavaScript: This allows the website to become dynamic and interactive.
  • Interaction with APIs: The website can invoke external APIs allowing complex applications to be created.
  • Automated build / deployment: JAMstack applications can be integrated with an automated build pipeline where a developer works locally to build and test the application. When the release version is checked in an automated build workflow is started which handles the generation of the website and then automatically deploys the site .

A few observations:

  • With JAMstack, a static site generator is simply a tool used to generate a static website.
  • JAMstack applications are more secure since they are static web sites with no backend server integration. Of course any APIs they communicate with may be vulnerable to attack but that is a separate issue.
  • JAMstack apps are more dynamic. They can make use of plain JavaScript or JavaScript frameworks and they may communicate with APIs. Since the front-end is hosted on a static site hosting or a CDN any APIs would normally be hosted elsewhere and would have the appropriate security and governance around them.
  • They are also SEO friendly.

JAMstack outlines a simple architecture for creating web applications. It takes advantage of modern JavaScript frameworks and the rise of microservices to support the creation of complex, dynamic, interactive applications. JAMstack applications use static site generators and introduce automation to trigger builds and deploy the application. The end result gives us most of the benefits of more traditional architectures.

Just as an aside I should point out that the website you are viewing this article on is actually built using JAMstack, with Nuxt.js as the static site generator and using Vue.js to provide enhancements and communicate with the external APIs.

Server Side Rendering (again)

Server side rendering process for web applications

Okay, so we're back to server side rendering. Server-side rendered applications (SSRs) in a sense take us back to where we were prior to the introduction of SPAs. In a server-side application the website content, the HTML pages that the users sees are generated on the back-end.

Proponents of SSR applications claim certain advantages. These include:

  • Security: Since pages are generated on the server there is no chance of extraneous and possibly sensitive data from API requests being exposed. The idea here is that APIs may not have been coded correctly or tested thoroughly. This could lead to the hypothetical situation where instead of a few select fields from a database query being returned via the API it returns all of the data, some of which could be confidential.
  • Performance: There is an argument, supported by limited testing, that pages are viewable on the browser earlier thus leading to a better experience for the user.
  • Reliability: The reliability of the application is better because JavaScript incompatibilities between browsers are reduced due to a much reduced dependence on JavaScript libraries and frameworks.
  • Measurement: The collection of metrics improves as user interaction with the page is co-ordinated with the server.
  • SEO: SEO is improved as the HTML is generated on the server.

Most of the supporters for using an SSR nowadays do not advocate a pure SSR approach but rather a hybrid approach that tries to marry the advantages of SSR with the better user experience of a modern single-page application. In this approach, the initial load, the first page load is generated on the server and the generated HTML page sent to the user's browser. The initial load however also delivers the JavaScript framework and the client-side application code and there will be a pause while the framework and application initializes, similar to the case with CSRs. The use of JavaScript allows, among other things, for the use of seamless routing between pages and the use of AJAX. This hybrid approach is frequently referred to as a Universal App.

The technology involved is a little different. Current SSRs frequently use Node.js to render the pages on the server prior to delivery to to the recipients browser.

In tests SSR applications are marginally faster. It should be noted that SSR is much more resource expensive on the server side compared to CSR. Under a minimal or light load SSR is slightly faster but it is not clear whether that advantage is maintained as the load increases. Common sense would suggest that all other factors being equal SSR performance would degrade faster.

SSR frameworks include:

  • Next.js
  • Nuxt.js

Conclusion

So, we have reviewed some of the modern web application architectures. Of course, in the real-world some of these terms will mean slightly different things depending on who you ask and also on which framework or toolset you are using but hopefully you have at least gained a slightly better understanding of what they are and how they relate to each other.

References

Jamstack A good resource for JAMstack enthusiasts. Has a very useful and comprehensive list of static site generators.

JAMstack.wtf Another useful source of information related to JAMstack.

Credits

Header image: Designed by fullvector / Freepik