How to use Vue.js in ASP.NET Core Razor views

Step up your Asp.Net Core game with Vue.js

Posted on October 12, 2020

Introduction

In this blog post I would like to share a cool and fun way to set up a client side framework in your Asp.Net Core web application.

Personally, I've written web applications using frameworks like React, Blazor (clientside and serverside) and Asp.Net. While I do enjoy working with pure client side web apps using an API backend, I tend to prefer working with a backend toolchain which serverside renders pages and spits out HTML for the users browser to render with minimal work.

With serverside rendering we also usually get awesome scores on Google pagespeed Insights, super SEO support, absolute control of data and we have a lot of resources available to us on the server. However, with a pure serverside rendered website, we also have disadvantages in the form of bad interactivity and increased load on the server for creating all that HTML.

So how do we keep our advantages of our old school serverside rendered Razor views and also create webapps with great interactivity? Well, by bringing in a sparkly frontend library like Vue.js. But how does one do this?

I'll answer this question in this blog post with a step by step guide plus give you some extra information about how to communicate between top level Vue components in your Razor view.


Getting started

In this section we will go through some simple steps and be up and running with Razor and Vue.js in no time.

If you don't care and just want to see the sample source code, you can get it here: Source code on Github

0) Dependencies

Before we begin, we need the dependencies for this project. Download and install them if you don't already have them on your machine.

1) Setting up a new ASP.NET Core project

With the .Net Core SDK installed on your machine, it's time to pop open a terminal and write

dotnet new mvc -n RazorVueDemo

This will create all the files for a standard Asp.Net Core Model-View-Controller project.

NewNetCoreProject

If you want, to see if everything is working, you can type in the terminal to build and run the project.

dotnet run

2) Scaffold a Vue.js project in ClientApp folder

Inside of our project folder we will create a new folder "ClientApp". This folder will serve as our root directory for our Vue setup. Again, we will open up a console. This time we will navigate inside of "ClientApp" and type the following to scaffold a new Vue app.

vue create razor-vue-demo

Go with this the babel preset when prompted and just press next.

If you have any issues with this step, you can refer to Vue's excellent documentation here: Vue.js docs

Vue will create a folder inside the ClientApp folder. Navigate inside the folder and copy all of the content to ClientApp. Delete the "razor-vue-demo" folder afterwards.

You can also safely delete the ".git" and "public" folder. You can also delete the App.vue aswell as the HelloWorld.vue components as we won't be using these.

Your project should look something like this.

NewVueProject

3) Setting up vue.config.js

Our Asp.Net Core Webapp is set up to serve static files from wwwroot. What we want to achieve is to configure our Vue app so it will build and bundle our Vue project into the wwwroot folder, so we can reference this Javascript bundle from our Razor layout page.

In the ClientApp folder create a a new vue.config.js file and paste in this code:

module.exports = {
  chainWebpack: (config) => {
    config.plugins.delete("html");
    config.plugins.delete("preload");
    config.plugins.delete("prefetch")
  },
  outputDir: "../wwwroot/bundle",
  filenameHashing: false,
  productionSourceMap: false,
  configureWebpack: {
    optimization: {
      splitChunks: false,
    },
    resolve: {
      alias: {
        vue$: "vue/dist/vue.esm.js",
      },
    },
  },
};

The most important part of this code for right now is to specify the outputDir for when we build the Vue project with "npm run build". This will create a "bundle" folder inside our wwwroot folder with our transpiled Javascript ready to be served over http.

The output from the console when running npm run build should look something like this:

TranspiledToWwwroot

4) Creating a Vue component

Alright, now for some fun. Lets create a simple Vue component. Inside of the ClientApp/src you will find a components folder. Create a "DemoComponent.vue" file and paste in this code.

<template>
  <h1>{{ msg }}</h1>
</template>
<script>
export default {
  name: "DemoComponent",
  props: {
    msg: String,
  },
};
</script>

This component is very simple. It takes a string as an input an displays it inside of a H1 tag.

5) Register our Vue component

We will need to register our component as a global component to be able to embed it directly into our Razor view. This can be done inside of the main.js file like so:

import Vue from "vue";

Vue.config.productionTip = false;

Vue.component(
  "demo-component",
  require("./components/DemoComponent.vue").default
);

window.Vue = Vue;

Also, we will need to add a reference to the Javascript bundle and set up a new Vue instance. We will do this in the shared _Layout.cshtml. Replace _Layout in the Views/Shared folder like so right below where the other Javascript is referenced.

<script src="~/bundle/js/app.js" asp-append-version="true"></script>
<script>new Vue({}).$mount('#app')</script>

We also need to define the HTML entry point for Vue to bind. We will do this like so in this demo:

<div class="container" id="app">
    <main role="main" class="pb-3">
        @RenderBody()
    </main>
</div>

All we're doing here is set the container div with the id="app". So just find that code and add the id="app" to the shared layout file.

6) Using our Vue component in a Razor view

All there is left now is to use out Vue component in the HTML. We can now do this on any page using the layout file. For now we will add the Vue components in the index.cshtml file inside the Views/Home folder.

Notice that we have a standard Razor view with a vue Component referenced in "kebab case" naming. This is important.

@{
  ViewData["Title"] = "Home Page";
}

<div class="text-center">
  <h1>Hello from serverside rendered blazor</h1>
  <demo-component msg="Hello from clientside rendered Vue component"></demo-component>
</div>

Replace everything in the file with these lines.

If everything went well, you should now be able to run your project using dotnet run and see this. Don't forget to run npm run build from inside the ClientApp folder first.

VueComponentRendered

That's it. We got Razor and Vue side by side.


Extra: How to communicate between "top level" Vue components

This is interesting. Because of the way we add components right into our Razor view they have no knowledge of other components that are not direct children to it.

We could solve this issue by introducing a state management library like VueX, but if we just want to keep it simple and cool we can simply rely on a "bus" to send messages between "top level" components.

A simple way of registering a bus would be like this in the main.js file.

export const bus = new Vue();

Now, in a simple Vue component we can send a message to the bus like this:

<template>
  <button v-on:click="click">Click me to send event</button>
</template>
<script>
import { bus } from "../main";
export default {
  name: "DemoEventComponent",
  data: function() {
    return {
      timesClicked: 0,
    };
  },
  methods: {
    click: function() {
      this.timesClicked++;
      bus.$emit("demoEvent", this.timesClicked);
    },
  },
};
</script>

Another component could then be set up to listen to specific messages on the same bus like so. Notice that we can register this easily in the Vue "created" lifecycle method. If we modify our existing DemoComponent it could listen to those events like this:

<template>
  <div>
    <h1>{{ msg }}</h1>
    <h2>{{ eventMsg }}</h2>
  </div>
</template>
<script>
import { bus } from "../main";
export default {
  name: "DemoComponent",
  props: {
    msg: String,
  },
  data: function() {
    return {
      eventMsg: "",
    };
  },
  created() {
    bus.$on("demoEvent", (clickCount) => {
      this.eventMsg = `Clickcount on button in another component: ${clickCount}`;
    });
  },
};
</script>

Don't forget to register our top level DemoEventComponent in main.js like this:

Vue.component(
  "demo-event-component",
  require("./components/DemoEventComponent.vue").default
);

And of course, add the new component to er Home page view so it looks like this:

@{
  ViewData["Title"] = "Home Page";
}

<div class="text-center">
  <h1>Hello from serverside rendered blazor</h1>
  <demo-component msg="Hello from clientside rendered Vue component"></demo-component>
  <br>
  <demo-event-component></demo-event-component>
</div>

And that's pretty much it. Check it out by running dotnet run again. Very simple and fun to work with.


Summary

In this short guide we have set up a simple Asp.Net Core webapp. We've added a Vue project inside of ClientApp folder and set it up to be transpiled into our wwwroot folder.

From this we've added a Vue component directly as part of our Razor .cshtml view and seen it rendered in our browser.

There is not much to it, but it really gives us an awesome tool to structure our frontend code and write reusable components while not being forced to write a full single page application and instead use the tools available to us in Asp.Net Core MVC.

I really enjoy using this approach because of very powerful toolchain available to us on the backend. Vue.js allows us to build on top of this serverside approach and add user interactivity to our page without having to learn a whole new technology stack from scratch.

If you're interessted, the full source code is available here: Source code on Github

I hope you'll be inspired to try it our yourself.

Update

I've done a follow up on this post showing how to upgrade this project to use Vue 3. Check it out the blog post here.

Jeppe Ærenlund profile image

About the author

Jeppe Ærenlund

I'm a former social worker who decided to switch to a tech career. Today I'm working as a specialized .Net backend developer and learning new stuff every day. I like to keep active and I hope this blog can help inspire new developers aswell.

If you liked this blog post consider supporting me by bying me a coffee.