There are so many different ways to create a web application today, it’s staggering. We have lots of different languages, different clouds, different server-side runtimes, and different deployment tools. Web assembly complicates things even more now that we can create our front-end in practically any language we want. In this post, we’ll try to make some semblance of order. We’ll go over the most popular technology choices in each layer of web application development, and see their pros and cons. That includes the client-side tech, the backend server, the ways to deploy to Azure, the CI/CD pipeline, the database, and the login mechanism.

The only constraint we’ll have is that we have to deploy to Azure and we have to use .NET. Why? First of all, without some constraints, I would have to write several books to cover every type of web technology. But other than that, I love C# and Azure and I have experience with them, so working with a different cloud or a different language doesn’t make sense for me, and if you’re reading this blog, then you’re probably the same.

Let’s start with choosing the front-end technology.

Front-end technologies

There was a time that a new JavaScript framework sprang to life every other day. I’d love to say that that period is over, but it’s not entirely true. The appearance of Web Assembly caused a new gold rush and now it seems everyone is writing a runtime for their favorite language. But at least we are somewhat agreed on SPAs and MVC frameworks. But I’m getting a bit ahead of myself, so let’s break things down. There are three types of directions you can take when choosing a front-end framework.

There are other options, but we’ll talk only about these because they are the ones the community and Microsoft seem focused on.

Single Page Application (SPA)

A SPA means the entire front-end side is a single static bundle of HTML, CSS, and JavaScript. The interaction with the server is to fetch data via REST calls. The user will download the bundle once and then navigating from one page to another won’t require additional roundtrips to the server.

There are lots of advantages to this approach. Once you’ve downloaded the bundle, the app can be very responsive because its only constraint is the client resources and not the network (other than fetching data). Since the bundle is static, you can push it to a CDN and get fantastic download speeds. The bundle is cached by the browser, so the user will have to download it just once, at least until your next deployment. Another advantage of a SPA is that the JS frameworks and ecosystems are extremely mature. The popular frameworks like React.js, Vue.js, and Angular are very well supported and documented with a huge community to each. You have an ecosystem of bundlers (like Webpack), linters (like ESLint), test runners (like Jest), and transpilers (like Babel) that make all aspects of developing effective and enjoyable.

Here’s an example of React components:

function Car(props) {
  return <p className="car">Car model: { props.brand }</p>;
}

function Garage() {
  const carName = "Tesla";
  return (
    <div>
      <h1>Cars in my garage:</h1>
      <Car brand = { carName } />
      <Car brand = "BMW" />
    <div/>
  );
}

Choosing between React, Angular, and Vue.js is a whole other discussion, but they are all good choices with a lot of successful projects for each. Having said that, it seems that React has won the public opinion as to the most loved web framework .

Server Rendered Application

A server rendering concept means that the server generates the HTML, CSS, and JavaScript for each page. So while in a SPA, your JavaScript issues an AJAX call to fetch data from a server, in an ASP.NET Core application, the server will respond with an HTML that already includes the needed data. This can be good for performance in certain scenarios because the server generates the HTML code instead of JavaScript running on the client. A user with a slow device can have a bad experience with a SPA that takes a long time to run JavaScript and render HTML tags, whereas during server rendering that part is done by your fast server computer.

Tip: There are ways to server-side-render SPAs as well.

We have several options in this category, even if we narrow it only to .NET:

  • ASP.NET MVC Core is the classic way of building an app. You split your C# classes into Controllers, Views, and Models. Each Controller represents an endpoint or a few endpoints. Each View represents a UI page written in Razor, which is a combination of C#, HTML, JavaScript, and CSS that works surprisingly well. There’s inherit support for components, layout views, authentication, middleware, and a hundred other things baked in for your convenience.

    The downside is that it might seem a bit over-engineered and bloated, especially for someone coming from Node.js.

Controllers/HelloWorldController.cs

public class HelloWorldController : Controller
{
	// GET: /HelloWorld/
	public IActionResult Index()
	{
		ViewData["Message"] = "Brush your teeth every day!";
		return View();
	}
}

Views/HelloWorld/Index.cshtml

@{
    ViewData["Title"] = "Hello world";
}

<h1>Important message:</h1>
<h2>@ViewData["Mesasge"]</h1>
  • Razor pages is similar to ASP.NET Core MVC, but more simplified. In this model, the routing is done according to the file names and folder names of your Razor .cshtml files. There are no Controllers at all. Each Razor page is accompanied by a code-behind .cshtml.cs file, called a page model, which is a combination of a ViewModel and a Model. It contains event handlers (like for Form submit) and the data itself. The result is a much cleaner code base, removing a lot of the ceremony ASP.NET MVC has.

    Razor pages seem to get less focus from Microsoft than ASP.NET MVC or Blazor, at least as seen from the outside, which makes it a riskier choice.

Pages/HelloWorld.cshtml

@page
@model HelloWorldModel
@{
    ViewData["Title"] = "Hello world";
}

<h1 class="display-4">Welcome @Model.Name</h1>

Pages/HelloWorld.cshtml.cs

public class HelloWorldModel : PageModel
{
    public string Name => (string)TempData["Name"];
    
	public HelloWorldModel()
	{
		TempData["Name"] = "Bill Gates";
	}
}
  • Blazor server is quite a different animal. It allows to develop web apps with C# instead of JavaScript. Whenever there’s something that requires code to run, like a click event handler, the client calls the server via SignalR , and C# code runs on the server. Another aspect of Blazor is that the client always downloads only the part of the page’s HTML that needs to be rendered. If the user scrolls down, the client sends a request to the server, which generates the new part of the view and returns it to the client.

    I admit, it sounds a bit over-complicated, but according to all accounts I heard, it works really well. The syntax is also very nice, including a great binding model and the ability to break down everything into small components, similar to SPA frameworks like React. As you can imagine, this approach has a lot of pitfalls, such as every interaction having to do a roundtrip to the server and back. But if you really don’t want to write JavaScript, Blazor server might be right for you.

Pages/Counter.Razor is a component:

@page "/counter"

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    [Parameter]
    public int IncrementAmount { get; set; } = 1;

    private void IncrementCount()
    {
        currentCount += IncrementAmount;
    }
}

Index.Razor is the parent that contains the component:

@page "/"

<h1>Hello, world!</h1>

<Counter IncrementAmount="10" />

Don’t know about you, but this seems much nicer to me than ASP.NET MVC’s views, controllers, and models combo. Kind of reminds Vue.js single file components, doesn’t it?

Blazor WebAssembly (Blazor Client)

The last option, Blazor Client , uses Web Assembly technology, which allows writing code in any language in the browser. Well, it’s a bit more complicated than that, and involves having to download and run a C# runtime. But the result is being able to run C# code on the client-side. The disadvantage is that the user will have to download this runtime and even the smallest app will be pretty sizeable (over 2MB for a ‘hello world’ app).

The pro is a fast and responsive client application that’s written entirely in C#.

One cool detail is that the code for Blazor Client and Blazor Server are pretty similar. Up to a certain point, after all, Blazor Server runs on the full .NET runtime, whereas Blazor WebAssembly runs in a minimized WASM Mono runtime.

What about ASP.NET Web API?

In this post, we’re choosing only the front-end technology of our application. Granted, the server rendering technologies are both client and server side. But the other choices (SPA and Blazor Client) need a server side, and that’s exactly where ASP.NET Web API comes in. We also have other options like ASP.NET Core Minimal APIs and Azure Functions , but we’ll talk about all that in the next part of this series.

Making a choice

Now that we covered all the popular options, it’s time to choose. Like everything in software development, the correct choice is always “It depends”. But I’ll try to give you a quick (opinionated) overview of each choice and what might fit better in different cases. Here are our candidates again:

  • Single page application – I feel choosing this option is not going to be wrong in any scenario. Using SPAs is so powerful that it practically became the industry standard for web applications. It has the lowest latency during user interaction, it can be deployed to a CDN, and the mature frameworks are a joy to develop. The downsides would have to be that you’ll have to write JavaScript or TypeScript, which isn’t ideal if you prefer C#.

  • ASP.NET Core MVC – I have mixed feelings about ASP.NET MVC. On one hand, I feel it’s somewhat over-engineered and opinionated. I have to write too much code and create too many files for each endpoint. The controllers can become bloated, especially when you start using dependency injection to consumer different services for different endpoints.

    On the other hand, MVC is very mature and has a lot of advantages. There are many developers familiar with it, there’s lots of documentation and a large community, and it’s a bit easier to deploy since you have just one project.

    Overall, I don’t love this option because in each application scenario I would prefer something else. For small apps and internal tools, I would choose Razor pages for simplicity or Blazor. For big applications, I would prefer a SPA framework like React with a .NET API. One scenario where I would prefer MVC is if my development team already has a lot of experience with it.

  • Razor pages – This is a relatively new technology and the jury is still out on this one. I think it’s promising but I wouldn’t bet the farm on it at this time. I do think it’s a lower risk than something like Blazor because it’s similar under the hood to what we’ve been doing for a long time in ASP.NET MVC. It seems thinner and easier in terms of fewer files and less ceremony around each endpoint.

    On the upside, you’ve got the simplest technology of the bunch.

  • Blazor Server – This looks pretty amazing but the big caveat is that each interaction requires a roundtrip to the server. I think it can be a decent choice for an internal tool but I wouldn’t recommend using it for a major B2C app. At least not until we see some more success stories at scale.

  • Blazor Client – This is certainly the coolest and most innovative tech we talked about. But as mentioned, the big downside of Blazor WebAssembly is that you’ll have to download the MONO .NET runtime the first time you use it, which can make a simple app’s download size over 3MB. I think this can be a good choice with a team that’s very experienced in C# and very reluctant to move to web technologies. Possibly a good choice for porting .NET desktop apps to the web. I’d be very reluctant to write my company’s main product in Blazor Client just yet.

For some reference on the community’s direction, here’s Google Trends for the past 24 months when comparing Razor pages, Blazor, and ASP.NET MVC:

Blazor vs Razor pages vs Asp Net Mvc

ASP.NET, in all its configurations, is in the lead, followed by Blazor. This is somewhat of a moot point since Blazor is also part of ASP.NET, but I think most people don’t search for “Blazor ASP.NET”. Razor pages is way behind.

By the way, here’s what happens if I add React:

Blazor vs Razor pages vs Asp Net Mvc vs React

I admit it’s a bit unfair because React isn’t limited to just .NET. But man, what a difference…

What’s next?

After choosing your front-end tech stack, the next choice is your server-side technology, where there are a bunch of options as well. We mentioned ASP.NET Web API, Azure Functions, and the newer Minimal API model, but all that in the next blog post .

Finishing up

I hope this post helped in some way, possibly making sense of all the different pathways you can take when building a web app. Check out the next part where we’ll see all the great server-side options .NET has to offer.