Classic Web Site vs. Single-page Application (Part I: Development Cost)

Developing a web application using a framework such as Angular or React has been the norm nowadays. When you use one of those aforementioned frameworks, you typically create a so-called single-page application (SPA).

Typically, developing a SPA is more expensive because it involves more client-side development (using JavaScript or TypeScript) and still yet similar amount of development on server side. In this article, I’m looking closer at the cost of developing a SPA vs a classic web site.

I created two web applications: LongLiveRss – Classic Web Edition and LongLiveRss – SPA Edition. Both applications are of the same functionality: a feed reader with identical UI and features such as reading feeds, saving an article to your favorites, etc. The two applications differ mainly in the client side implementation. One utilizes SPA technology (Angular), another is a classic web site rendered with HTML and CSS (no JavaScript).

The following is the technology stack employed to build the two applications.

Classic Web Site SPA
MS SQL Server 2017 MS SQL Server 2017
Bootstrap 4 Bootstrap 4
Angular 7

The two applications share (either the code or the instance) several libraries: Services and Repositories libraries. Both also share the same instance of database. Diagram 1 shows how the data flows from the database to the view rendered by a browser. The components with grey shading indicate those with shared instance/code.

Diagram 1. Data flow in a classic web site vs. single-page application

Diagram 1. Data flow in a classic web site vs. single-page application

The cost to develop an ASP.NET MVC Controller is similar to that of an ASP.NET API Controller. A lot of code (such as ViewModel definition) can potentially be shared between the two. A Razor View looks a lot different than an Angular View, but the complexity is about the same.

The diagram shows that both applications employ similar components except that a SPA includes a component which has no counterpart in a classic web site: an Angular Component. The extra cost of developing a single-page application lies on this part.

Two Applications in One

I own a 1,017-page Pro ASP.NET Core MVC 2 book. I also own a 788-page Pro Angular book. A hasty estimation suggests that building a SPA costs 177% more than building a classic web site. More accurate estimate will require analyzing the complexity of server- and client-side code. Angular packs a lot of features that you can’t master overnight. It’s an opinionated framework which also requires somewhat advanced understanding of software design.

When you build an Angular-based SPA, you will write a lot of Angular code, so much that it feels like developing two applications. The Angular code is a big part of the application. Angular framework usage in a web application is rarely a (progressive) enhancement to a classic web site unlike the usage of jQuery in recent past.  When a browser doesn’t support JavaScript, a SPA will become unusable, while a classic web site with jQuery will (should) degrade gracefully.

Do Everything (Not Really, but Many Things) Twice

In SPA, many application infrastructures implemented on the server are done as well on the client side. Some examples are routing, authentication, and error handling.


When a visitor visits, say,, you need to program the Angular routing module to route that request to the correct Angular Component. And when an Angular service sends a request to the server at, you need to map an ASP.NET API method to handle that request.


Your Angular code needs to determine if a visitor has access to a certain view and handle it accordingly. Similarly, certain methods of your ASP.NET API needs to be protected from an unauthorized access.

Error handling

Inevitably you will write some sort of global error handler for your ASP.NET API. The Angular code will also need to handle error returned by the ASP.NET API or raised by some other Angular code.

Extra Work With Angular

Handling the back button used to be a lot of effort in a SPA. Nowadays, Angular handles that for you behind the scene. Plenty of things, though, come free with a classic web site, but still needs to be (re)implemented in a SPA.

Loading Progress Indicator

When a SPA sends a request to the server, there is no progress indicator shown by the browser while waiting for the response. You have to write your own implementation or incorporate many available packages out there.

Data Consistency

A SPA consists of different areas on the view. When data is updated in one subview, you have to program certain routine so that different subviews which reference the same data reflect the latest value.

Client-side Code Payload Size

When accessing a SPA, the browser has to download a lot more client-side code. (I analyze more of this in part 2.) Hence, optimizing the size of all those static JavaScript files is a complexity that a classic web site doesn’t have to deal with.



Angular vs. React vs. Vue (Part VI: Payload Size)

I took the three “To Do” apps built with Angular (version 6.0.3), React (version 16.4.1), and Vue (version 2.5.16), applied some form validation, and generated a production build for each app. In this article, I’m comparing the build size and the payload size of each app.

Framework Files Generated Number of Requests Payload Size (KB)
Angular 7 files (290 KB) 5 284
React 9 files (1.98 MB) 4 406
Vue 8 files (962 KB) 4 180
Table 1. Build artifacts and payload summary

Files Generated are the files produced by the build process and the ones to be deployed to the production server. React and Vue have a lot larger total file size because the build process of the two frameworks produce a .map file for every minified JavaScript file and/or CSS file.

Once the app is deployed to a production server, accessing the app on its URL triggers requests to several static resources (JavaScript, CSS file, etc); these requests are Number of Requests on Table 1 above, excluding requests to remote API.

The Payload Size is the total size of all those static resources. JavaScript files (the framework and application code) makes up the most of payload. The React app has the most payload, and Vue app the least.

Table 2. Build artifacts
Framework Filename Size (KB)
Angular 3rdpartylicenses.txt 2.12
favicon.ico 5.30
index.html 0.61
main.3584cb656b4d762e14c3.js 222.00
polyfills.2f4a59095805af02bd79.js 58.10
runtime.a66f828dca56eeb90e02.js 1.02
styles.34c57ab7888ec1573f9c.css 0.02
React asset-manifest.json 0.20
favicon.ico 3.77
index.html 0.64
main.65027555.css 0.09 0.28
main.8a73369a.js 400.00 1580.00
manifest.json 0.32
service-worker.js 3.18
Vue app.c9980c65.css 0.02
app.c4cbf4c9.js 4.15 19.30
chunk-vendors.d8813c49.js 173.00 762.00
favicon.ico 1.12
index.html 0.87

Angular vs. React vs. Vue (Part V: Form Validation)

Taking user input using a form is a standard feature in a single page application. In this article, I’m comparing how client-side form validation can be implemented in Angular (version 6.0.3), React (version 16.4.1), and Vue (version 2.5.16).

I’ve created a simple form with the following fields and the associated validations.

Field Name Validations
Name Required
Phone Must be in a specific format: XXX-XXX-XXXX
Username Must not be registered before (the actual validation is done on a remote server)
Password No validations
Confirm Password Must match Password field

Form with client-side validation

Sample Code


Angular comes with FormsModule which provides the framework to validate your form. When you add the directive NgForm to your form, the state of the controls within the form is tracked which allows invoking the validation process at the right moment. When a control enforce multiple validation rules, the specific violated rules are also tracked.

State Description
Touched Whether the control has been visited.
Changed Whether the control’s value has changed
Valid Whether the control’s value is valid.

Angular not only provides several built-in validators, but it also provides the framework to create a custom validator. This custom validator is implemented as a directive and the new validation rule can be added to a control by adding its selector to that control. When validation involves two controls (such as comparing the values in two controls), the custom validator directive (in this case, PasswordConfirmedValidatorDirective) must be applied on the form element. I’ve got more intuitive way in mind (as implemented in React and Vue app) to do it, but that’s what the documentation suggests.


React doesn’t come with any form validation feature. Its documentation doesn’t provide recommendation for third-party validation libraries either.

When I was looking for a React validation library, I found several libraries with different paradigms. Based on your programming experience, one paradigm will feel more intuitive than the other. I decided to use React Advanced Form to enable form validation on this app. I also installed the companion library react-advanced-form-addons which provides form controls with support to display validation errors.


Vue doesn’t provide a built-in form validation library, but its documentation provides an example how to do it by hand. It also lists a couple third-party validation libraries. I use Veevalidate for this Vue app. VeeValidate ships with a lot of built-in validation rules, including one that you can use to make sure a user enters new password correctly.

The form validation functionality provided by Angular is a good start toward a complete validation framework customized to your app. I also like the reusability of an Angular directive, but the boilerplate code adds so much noise.

Phone number validator. (Left to right: Angular, React, Vue)

Phone number validator. (Left to right: Angular, React, Vue)

To start from a scratch, React and Vue needs implementation of events (such as touched, changed, and valid) and only then we can implement the actual validation process. Finding a thirdy-party validation library may not be easy even when there are plenty of options. One solution seems more intuitive than then others and it feels like it’s better to pick the most basic one which can be used as a foundation.