Telerik blogs
OpinionT2 Light_1200x303

A comparison of Blazor and Vue when it comes to modern web development—a review of the pros and cons. Does Blazor stack up?

C# running in the browser you say?

Blazor WebAssembly has been released and brings an alternative to JavaScript for building modern, responsive web applications that run in the browser.

But how does it stack up compared to other, more established options for building "modern" web applications?

In this post we'll compare Blazor and Vue.js.

Note: Interested in how Blazor stacks up against React or Angular instead? Check out a comparison of Blazor and React here and a comparison Blazor of Angular here.

Specifically, we'll look at these key areas (for both Blazor and Vue):

  • Getting started
  • Building the UI
  • Passing data between components
  • Handling forms
  • Routing
  • Fetching data from an API
  • Pros and cons of each

Before we dig in, it's worth noting this article focuses on Blazor WASM, which runs in the browser using WebAssembly. That said, many of the points are equally valid if you're looking at using Blazor Server instead.

Explore Blazor: Free quick start guide to productivity with Blazor. Get our Beginner's Guide ebook

Vue—The Two-minute Overview

Vue is a JavaScript framework.

Vue prides itself on being "incrementally adoptable."

In its simplest mode, you can simply include the core Vue scripts in your application then set about building your components.

Beyond that, and for more complex applications, you can use Vue's own CLI to create (and ultimately publish) a Vue project.

As with most other JavaScript frameworks, Vue apps are built as a series of small components that you can then compose together to make bigger features (and ultimately entire applications).

You'll typically write your Vue applications using HTML, CSS and JavaScript (or TypeScript).

How Does Blazor Compare?

Blazor is a framework which also enables you to build client web applications that run in the browser, but using C# instead of JavaScript.

When you create a new Blazor app, it arrives with a few carefully selected packages (the essentials needed to make everything work) and you can install additional packages using NuGet.

From here, you build your app as a series of components, using the Razor markup language, with your UI logic written using C#.

To publish your app, you can use dot net's built in publish command, which bundles up your application into a number of files (HTML, CSS, JavaScript and DLLs) which can then be published to any web server that can serve static files.

When a user accesses your Blazor WASM application, a Blazor JavaScript file takes over, which downloads the .NET runtime, your application and its dependencies before running your app using WebAssembly.

Blazor then takes care of updating the DOM, rendering elements and forwarding events (such as button clicks) to your application code.

Creating a New Vue App

There are two primary ways to start using Vue.

First, you can simply reference the scripts (via a CDN) and start adding components to any HTML page in an existing app.

<!-- development version, includes helpful console warnings -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

Alternatively, you can install the Vue CLI:

npm install -g @vue/cli

Then create and launch a new project:

vue create hello-world
cd hello-world
npm run serve

When you use vue create you'll be given various presets to choose from or you can pick and choose from options like enabling support for TypeScript, unit tests, etc.

Creating a New Blazor App

For Blazor, you can use Visual Studio or spin up a new project via the command prompt.

dotnet new blazorwasm
cd blazorwasm
dotnet run

You have a few other options, like the ability to include infrastructure for authenticating users, and whether to host your Blazor app in an ASP.NET web application, but the command above is the simplest option to get started.

Building Your UI with Vue

Vue is all about templates. Here's an example:

<div id="app">
    <label>What's your name?
        <input v-model="name" placeholder="Your name..."/>
    </label>
    <span>Hello {{ name }}</span>
</div>

<script src="https://unpkg.com/vue"></script>
<script>
    var app = new Vue({
        el: '#app',
        data: {
            name: ''
        }
    })
</script>

The template is contained within our #app div.

We then create a new Vue app and tell it to use the #app div as its target element.

v-model sets up a binding between the text input and name data property.

As a result, name will always reflect what the user enters into the text input, and if the value of name is changed programmatically this will be reflected in the text input.

We've used the {{ name }} syntax to render the current value of name so we can see it change instantly as we type new values into the text input.

Vue example showing how typing a name changes the greeting

This will get you up and running, but in practice most applications will be made up of several components, composed together to make bigger features.

To turn this greeting into a reusable component, we need a slightly different syntax.

Vue.component('greeting', {
    data: function () {
        return {
            name: ''
        }
    },
    template: `
        <div>
            <label>What's your name?
                <input v-model="name" placeholder="Your name..."/>
            </label>
            <span>Hello {{ name }}</span>
        </div>
    `
})

There are some subtle differences between a Vue component and the app we started with:

  • We've moved the markup into a template attribute
  • data in a component is expressed as a function which returns an object

With these changes we can now render this component wherever we like in our application.

<div id="app">
    <greeting/>
</div>

In summary, a Vue application:

  • Can be added to an existing HTML page
  • Comprises a Vue app and optionally one or more components
  • Is written using JavaScript and HTML
  • Runs as JavaScript in the browser

Building Your UI with Blazor

Blazor also encourages you to break your UI down into a number of smaller components.

Unlike Vue, you write your components using Razor and C#.

<label>What's your name?</label>
<input type="text" @bind-value="Name" @bind-value:event="oninput" placeholder="Bob"/>
<span>Hello @Name</span>

@code {
    public string Name { get; set; }
}

We've got roughly the same markup, but this time we have used Blazor's @bind syntax to bind our input to a property called Name.

When the user enters their name, the Name property will be updated with the value they enter.

By default Blazor would update the value of Name on blur (when we clicked out of the text input), so we've added @bind-value:event="oninput" to make it update the property as soon as we start typing.

You can now render this component wherever you like in your application...

<h1>
    A brief introduction to Blazor...
</h1>

<Greeting />

Blazor example showing how typing in a name changes the greeting

In summary, a Blazor UI:

  • Comprises one or more components
  • Is written using Razor and C# (which takes your markup and data, and combines them together)
  • Runs on WebAssembly in the browser

Passing Data Around—Vue

We've already seen one way Vue can handle data, storing name directly in our greeting component.

var app = new Vue({
    el: '#app',
    data: {
        name: ''
    }
})

The other common option is to pass data in to a component.

Say we wanted to pass a headline to our greeting component:

<greeting headline="Welcome, thanks for being here!" />

Vue enables this via something called props.

Vue.component('greeting', {
    data: function () {
        return {
            name: ''
        }
    },
    props: ['headline'],
    template: `
        <div>
            <h2>{{ headline }}</h2>
            <label>What's your name?
                <input v-model="name" placeholder="Your name..."/>
            </label>
            <span>Hello {{ name }}</span>
        </div>
    `
})

We've added a props array to the component:

props: ['headline'],

This makes our component accept a headline value which we then render using standard interpolation syntax <h2>{{ headline }}</h2>.

Props are the key to unlocking reusable components, making it possible to use the same component in lots of different scenarios, passing different values in each time.

Vue example showing a prop being rendered

While using data and props works well for many scenarios, you may run into a need for more centralized state in your applications.

One option is to roll your own data "store," whereby you have a central "store" object which is then shared between multiple components.

Alternatively, once you've set off down that road you can just keep on walking until you get to Vuex!

Vuex offers a Vue implementation of the Flux pattern for state management (you may have heard of this other Flux implementation called Redux).

Crucially, as with everything, it pays to keep to the simplest possible solution that meets the needs of your specific application, but it's good to know more advanced options are out there if you need them.

Passing Data Around—Blazor

Broadly speaking, Blazor has the same two primary options for managing state.

You can store data in the component itself using properties (as with Name in our example) or take data in via parameters (as with Headline).

<h2>@Headline</h2>
<label>What's your name?</label>
<input type="text" @bind-value="Name" @bind-value:event="oninput" placeholder="Bob"/>
<span>Hello @Name</span>

@code {    
    [Parameter]
    public string Headline { get; set; }

    public string Name { get; set; }
}

As with the Vue example, when you render Greeting you can pass in a headline and it will be rendered accordingly.

<Greeting Headline="Welcome, it's still great to see you..."/>

For more advanced scenarios, much like Vue you can roll your own centralized data store for Blazor apps or check out emerging options for using the Flux pattern with Blazor via projects such as Fluxor.

Handling Forms in Vue

We've already seen the core mechanism Vue employs for handling forms: the v-model directive.

You can use v-model to bind most form inputs to data, including text inputs, selects, checkboxes, etc.

Here's the markup for a simple contact form.

<div id="app">
    <form @submit.prevent="submit">
        <label>
            Name:
            <input type="text" v-model="name"/>
        </label>
        <label>
            Thoughts?:
            <input type="text" v-model="comments"/>
        </label>
        <input type="submit" value="Submit"/>
    </form>
</div>

v-model does the heavy lifting: keeping your component's data in sync with the values entered by the users.

We direct this form to a submit method in the Vue component using @submit and the optional prevent qualifier to prevent the default browser submit behavior.

Here's the corresponding JavaScript.

<script>
    var app = new Vue({
        el: '#app',
        data: {
            name: '',
            comments: ''
        },
        methods: {
            submit: function(e){
                // submit data to API
                console.log(`${this.name}:${this.comments}`);
            }
        }
    })
</script>

The submit method will be invoked when the user submits the form, and there we log out the values for name and comments.

For validation, you'd either need to write your own logic in the submit method, or lean on unofficial Vue libraries such as vuelidate and VeeValidate.

Handling Forms with Blazor

Blazor has built-in functionality to handle your form data and validation using something called EditForm.

Here's the markup for an equivalent contact form.

@using System.ComponentModel.DataAnnotations

<EditForm Model="FormModel" OnValidSubmit="HandleValidSubmit">
    <DataAnnotationsValidator />
    <label>
        Name:
        <InputText id="name" @bind-Value="FormModel.Name"/>
    </label>
    <label>
        Thoughts?:
        <InputText id="comments" @bind-Value="FormModel.Comments"/>
    </label>
    <input type="submit" value="Submit"/>
    <ValidationSummary />
</EditForm>

We've replaced the standard HTML input elements with Blazor's InputText component.

Technically it's entirely possible to use standard HTML elements and forms with Blazor, but using the built-in controls makes certain things a lot easier (validation being one, as we'll see in a moment).

Where Vue used v-model, we have @bind-value, which similarly takes care of both reading the value from a field and updating it when a user types in a new value.

The form itself is based on a model of our choosing (where the form values will live), and we've told it which method to invoke when the form is submitted (and is valid).

Note how we've included a DataAnnotationsValidator and ValidationSummary; these components wire up the form to automatically respect any validation rules we set up on our model.

Here's the rest of the code:

@code {
    protected ContactUsModel FormModel { get; set; } = new ContactUsModel();

    async Task HandleValidSubmit()
    {
        // post to your API
        Console.WriteLine($"{FormModel.Name}: {FormModel.Comments}");
    }

    protected class ContactUsModel
    {
        [Required]
        public string Name { get; set; }
        public string Comments { get; set; }
    }
}

The ContactUsModel class could live anywhere in our project.

We have a FormModel property and HandleValidSubmit method.

When someone fills in the form, if they've met the validation rules (a Name has been entered) then HandleValidSubmit will be invoked.

Otherwise, the ValidationSummary will be used to show which fields have validation errors.

Blazor example of form with validation errors

Overall the Vue and Blazor form implementations share a lot of similarities:

  • They both employ a two-way binding syntax for inputs
  • They both offer a means to handle form submissions via a method

Where Blazor stands apart is in its built-in validation support, using the well-established DataAnnotations library and some new Blazor helper components.

Routing in Vue

Vue offers a separate router you can plug in to your application.

You can include it in your HTML page:

<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>

You're then able to render a router-view in your markup.

<router-view></router-view>  

This is where Vue will render content as you move between routes.

You can configure routes in the JavaScript for your app.

<script>    
    const Home = { template: '<div>Home</div>' }
    const Contact = { template: '<div>Contact Us</div>' }

    const routes = [
        { path: '/home', component: Home },
        { path: '/contact', component: Contact }
    ]

    const router = new VueRouter({
        routes: routes
    })

    const app = new Vue({
        router
    }).$mount('#app');
</script>

Here we have two components (Home and Contact).

Then we've declared two routes pointing to these components.

Next, we declare a router and assign our routes to it.

Finally, we create a new Vue app, using the router.

With all this in place, you can now navigate to these two components using the # symbol.

  • <your-site-here>/index.html#/home</your-site-here>
  • <your-site-here>/index.html#/contact</your-site-here>

You will often need to pass further data along in the route. For example, if you're routing to a details page for a product, you would expect to provide a product id in the route...

  • <your-site-here>/index.html#/product/1</your-site-here>

You can configure your routes to accept a parameter:

routes: [
    { path: '/product/:id', component: User }
]

You can then retrieve this id using $route.params.

<h2>
    Displaying product details for {{ $route.params.id }}
</h2>

With a little more plumbing you could also capture these route parameters via your component's props and avoid having to use $route.params everywhere in your components.

Routing in Blazor

Blazor includes routing "out of the box". If you want to make a component "routable" you can simply add a @page directive...

@page "/GreetMe"

<h1>
    Welcome!
</h1>

Now any request to http://<your-web-site-here>/GreetMe will render this component.

You can also pass data in via the route and capture it in a parameter, like this:

@page "/GreetMe/{Name}"

<h1>
    Welcome @Name!
</h1>

@code {
    [Parameter]
    public string Name { get; set; }
}

Now any request to http://<your-web-site-here>/GreetMe/Jon will render a more personalized greeting (well, if your name's Jon according to this example!)

Fetching Data from an API Using Vue

Chances are your web application will need to fetch data from an API at some point.

Vue remains agnostic on how you approach this, leaving you free to use the native fetch API or any of the many third-party libraries, such as "Axios."

The key is knowing when to make the call, and for this Vue offers a mount lifecycle hook.

<script>
new Vue({
    el: '#app',
    data(){
        return {
            tickets: null;
        }
    },
    mounted(){
        axios
            .get('api/Tickets')
            .then(response => (this.tickets = response));    
    }
})
</script>

Now when this component is mounted:

  • a request will be made to api/Tickets
  • the returned data will be assigned to tickets

Once we have the data, we can loop over it using Vue's v-for directive and render markup for each item.

<div id="app">
    <div v-for="ticket in tickets">
        {{ ticket.title }}
    </div>
</div>

Fetching Data from an API Using Blazor

With Blazor, you can use HttpClient for all your data-fetching needs!

Under the hood, this defers to the native fetch API, but you can generally just ignore that and use the abstraction.

Here's an example:

@using System.Net.Http
@inject HttpClient Http

@foreach(var ticket in _tickets){
    <div>
        @ticket.Title
    </div>  
}

@code {
    private Tickets[] _tickets;

    protected override async Task OnInitializedAsync(){
        _tickets = await Http.GetFromJsonAsync<TicketSummary>("api/Tickets");
    }
}

OnInitializedAsync is broadly equivalent to Vue's mounted() lifecycle hook and will run when our component first loads.

Notice how we're able to use GetFromJsonAsync, passing in a Type to automatically deserialize the results of the HTTP call into an instance of TicketSummary? This is where Blazor has one significant advantage over JavaScript frameworks...

Shared Models—Blazor's Super Power?

Because you're writing your web app in C#, you can use the same data models in your frontend and backend (API) code.

Let's say for example you need to retrieve a list of people...

Diagram showing how it works using one shared model between client and API with Blazor

The Person model lives in a shared class library.

Both your Web API and Blazor Client projects reference this shared library.

Now your API can be strongly typed, returning (and accepting) data using the Person model.

The data is still serialized and sent "over the wire" as JSON data, but your Blazor application can deserialize the JSON data using the exact same Person model that was used to serialize it in the first place.

Now if you make breaking changes to your Person model, you'll immediately see compilation errors if you've introduced any breaking changes which affect either the client or server use of the model.

Pros and Cons

Now that we've seen them both in action, which one should you choose?

Naturally it's hard to make direct comparisons and which one you prefer will largely depend on your own background, skills and preferences.

Saying that, we've seen a number of similarities but also a few key differences between the two.

Vue

Vue is lauded for its light touch compared to other frameworks.

You can easily add Vue to an existing application, opening the door to incrementally improving your app without rewriting the entire thing.

The Vue CLI then comes into play if you do decide to structure your entire app around Vue, abstracting away the complexities of setting up a JavaScript build environment.

Vue Pros

  • Well-established framework with a battle-tested component model
  • The Vue CLI simplifies the JS build process
  • Lighter touch library compared to other frameworks such as Angular (the core Vue library handles the essentials with tangential features like routing available in separate libraries)
  • Can be incrementally added to enhance existing applications
  • You're free to plug in any other JS libraries you might need for your app
  • A large existing JS library ecosystem to lean on
  • Extensive documentation available

Vue Cons

  • Vue is largely unopinionated about how your application should be structured (only a con if you prefer a more opinionated, prescriptive approach)
  • JavaScript! (if you don't like it)
    • Vue does a great job of simplifying the data-binding aspects of building your web app, but in the end you're still writing JavaScript!
  • While the Vue CLI abstracts some of the details away, if you do decide to build your entire app around Vue, you'll be rubbing up against the JS ecosystem which brings its own complexity (build tools, package managers, compilation for different browsers)

Try Kendo UI for Vue—Complete UI component library for web apps. Free Trial

Blazor

Blazor has the obvious distinction that it uses C# instead of JavaScript.

This offers several advantages if you're coming from a C# background.

You can stick to the ecosystem you already know (NuGet, the dotnet tooling, Visual Studio or VS Code).

The ability to share models between client and backend API is a big deal and makes it much harder to inadvertently break your application.

Blazor Pros

  • Write modern web apps using C#
  • Built-in validation support for your forms
  • Ability to bring in third-party code via NuGet packages
  • You can use the tools you already know (Visual Studio, VS debugging, Intellisense, etc.)
  • Shared models significantly reduce the chances of accidentally breaking the client
  • You can use the same component model in the browser (using WebAssembly) or on the server (using Blazor Server)
  • Support to use the same Blazor component model on windows and for mobile development is coming

Blazor Cons

  • New framework, will take time to bed in and gain adoption
    • Tooling also young and will evolve over time
    • Fewer resources available on the internet (tutorials, etc.) compared to Vue at the time of writing
  • No obvious way to incrementally add Blazor WASM to your existing applications in a seamless fashion
  • Sizeable initial download of .NET framework to browser on first load
  • Does depend on WebAssembly support in the browser (although this is now widely supported)

One thing to call out here is the initial download time.

When someone accesses your Blazor WASM app for the first time their browser will download a version of the .NET framework as well as your application's files.

Once they have these files, they don't need to download them again, but it does mean you're likely to see a "loading..." indicator first time around.

The team at Microsoft has done a lot of work to get this initial download size down but naturally this means Blazor is better suited to some web applications than others.

You probably wouldn't want to use it for things like product landing pages where there's little to no business logic and it's imperative that the page loads as quickly as possible.

But, for any Line of Business apps, this initial download is unlikely to pose a major issue.

Try Telerik UI for Blazor—Native components for building web apps with C#. Free Trial

Over to You

Where Blazor fits into your plans will largely depend on your existing experience and how you feel about JavaScript.

If you're comfortable with JavaScript and the ecosystem, Vue is a solid framework which can easily scale up or down as your application requires.

On the other hand, if you already know and enjoy using C#, and have generally found JavaScript (the language and ecosystem) difficult to learn and live with, Blazor WASM is potentially a game changer.

So, are you thinking of using Blazor? Is it a contender or your next project, or will you be sticking with Vue for now?

The choice is yours!


Jon Hilton
About the Author

Jon Hilton

Jon spends his days building applications using Microsoft technologies (plus, whisper it quietly, a little bit of JavaScript) and his spare time helping developers level up their skills and knowledge via his blog, courses and books. He's especially passionate about enabling developers to build better web applications by mastering the tools available to them. Follow him on Twitter here.

Related Posts

Comments

Comments are disabled in preview mode.