DotNetCurry Logo

Modern Web Development using ASP.NET Core template, Vue.js and Webpack

Posted by: Daniel Jimenez Garcia , on 8/16/2017, in Category ASP.NET
Views: 10554
Abstract: This article explores the official Microsoft template for ASP.NET Core that uses Vue as its client-side framework and gets you started on using modern tooling and libraries like Webpack, Babel or hot-reload.

If you read the DNC magazine regularly, you will probably be aware that we have covered .NET Core, ASP.NET Core and have given an idea of what’s upcoming for .NET web developers.

A lot has been happening lately in the .NET Ecosystem!

For example, consider the JavaScript landscape of frameworks to help you organize your client-side code. As of 2017, there is a lot of fragmentation and different options for .NET web developers out there!


One option increasing in popularity is Vue.js, which has already received some coverage in this magazine. Vue is a lightweight and performant framework for creating user interfaces that was originally created and open sourced by Evan You, after working for Google on Angular.

Are you keeping up with new developer technologies? Advance your IT career with our Free Developer magazines covering C#, Patterns, .NET Core, MVC, Azure, Angular, React, and more. Subscribe to the DotNetCurry (DNC) Magazine for FREE and download all previous, current and upcoming editions.

Microsoft templates for ASP.NET Core

This article explores the official Microsoft template for ASP.NET Core that uses Vue as its client-side framework, which is also a part of their JavaScriptServices project. It should get you started on the right track without manually configuring modern tooling and libraries like Webpack, Babel or hot-reload.

Even if you are not particularly interested in Vue.js, I think you will find plenty of useful information on topics like Webpack and debugging that would apply to other project templates for Angular and React.

You can download the article code from GitHub

Why Vue?

You might be wondering why do you need to worry about yet another JavaScript framework when you already know the likes of Angular or React. And the answer to that question would be that if you are already using Angular or React and you are happy with them, then you probably don’t need to worry about Vue.

However, you might be now evaluating different options for your next project, or the next de-facto stack for your company. Or maybe you are not entirely happy about your current framework, and you think something lighter/faster/simpler could be worth considering.

Of course, you might just be curious to learn what’s the fuss about this framework!

Editorial Note: If you want a detailed comparison of Vue vs Angular vs React, check out VueJS vs Angular vs ReactJS with Demos (

In Vue, you will find a very lightweight and performant framework focused at its core on the view layer. It is designed to be fast and optimized by default, using a light-weight virtual DOM implementation. According to the official comparison with other frameworks, you could think about Vue as having the performance of React js with a shouldComponentUpdate function automatically implemented for you on every component.

  • Apart from being fast, its declarative syntax using plain JavaScript objects makes building reactive user interfaces a breeze.
  • It comes with a very well-designed components module. Decomposing your UX into a tree of components is very straightforward.
  • You decide which style works better when it comes to mapping your components to files: Single .vue file? Separated files for the template, code and style rules? Inline definition of components?

Let’s check an example from the official Vue guide (try it on jsFiddle here):

HTML code:

<div id="app">
      v-for="item in todoList"

JS code:

Vue.component('todo-item', {
  props: ['todo'],
  template: '<li>{{ todo.text }}</li>'
var app = new Vue({
  el: '#app',
  data: {
    todoList: [
      { id: 0, text: 'Learn Vue' },
      { id: 1, text: 'Download ASP.Net Core 2.0' },
      { id: 2, text: 'Join the Node.js meetup' }

The best part is that Vue doesn’t really end in the view layer.

Vue is designed so you can adopt its core library and use it for building your view layer, but at the same time, it can be extended with plugins and components that let you build complex SPA experiences.

This puts you in control of what you need to exactly bring into your project, with absolute liberty, to pick and match depending on your needs.

A curated list of components and libraries is provided in the awesome-vue GitHub page, but of course you can use other (or your own) libraries.

Before we move on, let me point you again towards this comparison section of the official Vue documentation, as well as this comparison article on DotNetCurry by Benjamin. If you are still confused about how Vue might help you in your projects, use these articles to compare it against the most popular JavaScript frameworks.

Installing the SPA templates

The first thing we need to do is to install the SPA (Single Page Application) templates provided by Microsoft. Following their initial blog post, this is as easy as running:

dotnet new --install Microsoft.AspNetCore.SpaTemplates::*

Now if you run dotnet new in a console, you will see a few different SPA templates. In its current version, this package installs templates for Vue, Angular, React, Knockout and Aurelia:

installed-spa templates

Figure 1, installed "dotnet new" templates

Once installed, you are ready to go, simply run dotnet new <template-name> on the console.

Creating a new ASP.NET Core project with Vue

As we saw in the previous section, the official Microsoft SPA templates provide a template with Vue as the client-side framework.

To get started and create a new Vue project, simply run the following commands on the console:

mkdir new-project
cd new-project
dotnet new vue

That will generate a new ASP.NET Core project using the Vue SPA template. Now let’s run the project and verify everything is working.

aspnet-core- generated-project

Figure 2, the generated project


Figure 3, running the project

Running the project from Visual Studio 2017

Everything has been wired in the project template, so locally running your new application works Out of the Box as you would expect.

Open the project with Visual Studio 2017 and press F5.

  • NuGet and npm dependencies will be restored/installed if it’s the first time you opened and launched the project
  • The application will be built with the Debug configuration
  • Once ready, your browser will be launched and your application will be loaded.

The debugger is attached as you would expect, and that includes setting breakpoints both in the server-side code and the client-side TypeScript Vue code!

Note: Client-side debugging with VS would only work on supported browsers like Chrome and Edge!


Figure 4, breakpoint on a TypeScript file in Visual Studio 2017

Running the project from Visual Studio Code

Visual Studio Code (VS Code) is one of the better products Microsoft has come up in recent times and it is no surprise it is being increasingly adopted by many developers. I have personally been using it for creating several web applications over the last year and I am enjoying every aspect of it.

How well does VS Code play with our newly created Vue project?

You need to implement a few steps before you get the same debugging experience of VS 2017, in VS Code:

  • Make sure you have the C# and Debugger for Chrome extensions (
  • Run npm install on your project root to restore all the required node modules, like webpack and its dependencies
  • Follow the steps on this related article on Debugging ASP.NET Core SPA in Visual Studio Code to create the necessary launch.json and tasks.json configuration files.

If you have any trouble following that article and creating these files (or don’t have the time for it), check the .vscode folder in the companion code in GitHub.

debugging-client side-vscode

Figure 5, debugging client and server side in VS Code

Running the project from the Command Line

Another valid option to start your new and shiny project without special debugging support is by running it directly on the command line.

  • You need to run dotnet restore && npm install after the project was created. This installs the required NuGet and npm dependencies.
  • To properly run in debug mode, you need to set the environment variable ASPNETCORE_ENVIRONMENT to “Development”. See the announcement blogpost for more details.

Then execute dotnet run on the command line and open http://localhost:500 in your browser. Stop with Ctrl+C once you are done.

Out of the Box features

In this section, we will take a closer look at some of the features, libraries and tooling included by default within the Vue template.

At a high level, what you have is a project made of:

  • ASP.Net Core backend
  • SPA frontend using Vue 2 and TypeScript
  • Bootstrap 3 styling
  • webpack generating the js/css bundles ultimately send to the browser, wired for both development and production modes.

Let’s start somewhere familiar and check the Index method of Controllers/HomeController.cs. All this does is rendering the Index view. Now let’s check the Index view and you will see it does very little:

- Renders the html element (see that div with id app-root?) where our Vue app will be mounted

- Loads the Webpack bundle that contains the transpiled JavaScript code of our Vue application

    ViewData["Title"] = "Home Page";

<div id='app-root'>Loading...</div>

@section scripts {
    <script src="~/dist/main.js" asp-append-version="true"></script>

If you are unfamiliar with webpack, we will take a closer look at it later; for now, see it as the piece of the project that takes all the TypeScript/JavaScript and CSS/LESS files of the client-side code and produces the combined bundle files that are sent to the browser.

Ok, so now let’s open ClientApp/boot.ts. This is the entry point for our client side code, the file that contains the code starting the Vue application in the browser:

import 'bootstrap';
import Vue from 'vue';
import VueRouter from 'vue-router';

const routes = [
    { path: '/', component: require('./components/home/home.vue.html') },
    { path: '/counter', component: require('./components/counter/counter.vue.html') },
    { path: '/fetchdata', component: require('./components/fetchdata/fetchdata.vue.html') }

new Vue({
    el: '#app-root',
    router: new VueRouter({ mode: 'history', routes: routes }),
    render: h => h(require('./components/app/app.vue.html'))

As you can see, it is loading the main 3rd party libraries we depend on (mainly Bootstrap and Vue) and starting the Vue app attached to the html element #app-root. Remember, this element was rendered by the Home/Index.cshtml view.

It is also establishing the client-side routes by instantiating its own VueRouter. This means when you navigate to one of those pages like http://localhost:5000/counter, the vue specific router will take care of instantiating and rendering the vue component found in ClientApp/components/counter/.

And where will it be rendered?

Well, if you take another look at the template inside app.vue.html, which is the template for the main app component instantiated in boot.ts, you will notice it has a placeholder for <router-view></router-view>:

    <div id='app-root' class="container-fluid">
        <div class="row">
            <div class="col-sm-3">
                <menu-component />
            <div class="col-sm-9">

Wait a second!! This means we have client-side routes and server-side routes?

Yes, that is correct!

But what if I navigate directly to http://localhost:5000/counter, won’t that be handled by the ASP.NET Core routing and return a 404?

This is taken care by how the routes are being wired on the server side. Open Startup.cs again and take a look at the routes being registered.

You will notice a spa-fallback route being added at the end:

app.UseMvc(routes =>
        name: "default",
        template: "{controller=Home}/{action=Index}/{id?}");

        name: "spa-fallback",
        defaults: new { controller = "Home", action = "Index" });

This routing helper is part of Microsoft’s JavaScript services, more concretely of their Microsoft.AspNetCore.SpaServices package. So when a request like localhost:5000/counter arrives, the following happens:

1. The static files middleware is executed and does nothing since it doesn’t match any static file.

2. The routing middleware is executed next, and tries the first default route. Since the url doesn’t match to any known controller-action pair, it does nothing.

3. The next route is executed, the spa-fallback one. And this will basically execute Home’s Index action, which in turn renders the #app-root element and our client-side app code. This way when the browser executes our vue startup code part of boot.ts, our VueRouter will react to the current url by rendering the counter component!

The only exception made by the spa-fallback route is when the url appears to be something like http://localhost:5000/missing-file.png.

In this case, it will do nothing and the request will end in 404 which is probably what you want. This might be a problem if your client-side routes contain dots, in which case the recommendation is that you add a catchall MVC route rendering the Index view. (But bear in mind in this case requests for missing files will actually render your index view and start your client side app).


Webpack is quite an important piece in all this setup. It is the bridge between the source code of your client-side app located in /ClientApp, and the JavaScript code executed by the browser.

If you are new to webpack, I think by now you will have at least two questions if not more:

  • The source code of the client side app is written in Typescript. How/where is this converted to JavaScript before being sent to the browser?
  • The source code of the client app is structured in multiple files. Furthermore, most components have separate template and TypeScript files. However, in our razor views, we only render a single script with src= "~/dist/main.js". How is this file created?

This is all webpack’s work behind the scenes. Webpack is defined as a module bundler meaning it understands your source code composed of many individual files (TypeScript, JavaScript, less, sass, css, fonts, images, and more!) and produces a combined file ready to be used in the browser.

How this happens, is defined in a couple of files on your project root folder named webpack.config.js and webpack.config.vendor.js.

Let’s start by inspecting webpack.config.js.

Exploring webpack.config.js

See the entry property of the configuration object? That’s the initial file you point webpack to for each of the bundles to be generated, and it will then find your other files through the import statements on them.

We are basically configuring a single bundle named “main”, to be composed of files starting from ClientApp/boot.ts:

entry: { 'main': './ClientApp/boot.ts' },

Now jump to the output property.

Remember that in the razor Index view there was a single script statement with source equals to ~/dist/main.js? That was because we are telling webpack to generate the bundles inside the wwwroot/dist folder of the project and use the name of the bundle as the file name:

output: {
    path: path.join(__dirname, bundleOutputDir),
    filename: '[name].js',
    publicPath: '/dist/'

So the entry property defines the bundles that will be generated (in this case a single one named main) and the output property defines where these bundles will be physically generated.

The next stop is the module property of the configuration. By default, webpack only understands JavaScript code.

However, our source code is made of TypeScript files, html templates, css files, etc. We need to tell webpack how to interpret these files so they can be processed and included in the generated bundle!

We do so by configuring rules that match certain file types with specific loaders.

module: {
    rules: [
        { test: /\.vue\.html$/, include: /ClientApp/, 
          loader: 'vue-loader', 
          options: { 
              loaders: { js: 'awesome-typescript-loader?silent=true' } } },
        { test: /\.ts$/, include: /ClientApp/, 
          use: 'awesome-typescript-loader?silent=true' },
        { test: /\.css$/, use: isDevBuild ? 
          [ 'style-loader', 'css-loader' ] : 
          ExtractTextPlugin.extract({ use: 'css-loader?minimize' }) },
        { test: /\.(png|jpg|jpeg|gif|svg)$/, use: 'url-loader?limit=25000' }

These loaders know how to treat these files. For example:

  • The vue-loader understands files divided into <template>, <script> and <style> sections and interprets each of those parts
  • The awesome-typescript-loader understands TypeScript files and will transpile them into JavaScript using the options in the tsconfig.json file of your project.

Finally, we have the plugins section where we can configure webpack for more complex work. For example:

  • We can let webpack know about a separated “vendor” bundle that will contain libraries code like that of Vue or Bootstrap, to keep our main bundle containing only our app specific code.
  • We can add source maps when generating the bundles for development, to debug the generated bundles in the browser
  • We can optimize the bundles for production further minifying and uglifying the produced bundles.

By now you will probably be curious about what the separated webpack.config.vendor.js file is for. This is a similar file that takes care of processing 3rd party libraries like Vue or Bootstrap, generating a couple of files vendor.js and vendor.css files at the end.

If you take a look at the razor _Layout.cshtml file, you will notice it is rendering a stylesheet with href="~/dist/vendor.css" and a script with src="~/dist/vendor.js". These files are generated there when webpack is run using the webpack.config.vendor.js settings.

There is nothing new on that file, except maybe that CSS code is extracted into its own file (vendor.css) rather than being inlined in the same file (like it happens for the main bundle).

Note: This setup assumes you will rely on bootstrap default styles with a few small custom rules on your specific code, but if you had large style definitions, you might consider modifying webpack.config.js to also extract css rules into its own main.css file.

There is a lot more to webpack and many other options/scenarios available than what I have just covered here. Hopefully it will be enough to give you a rough idea of how things work.

If all of this seems very confusing, don’t worry; you are not the only one feeling that way. It will make sense as you use it more and come across more scenarios where webpack can help you.

Webpack in development

We have been running the project earlier and we didn’t have to generate those webpack bundles. In fact, we didn’t have to worry about webpack at all.

This is because of a very helpful middleware part of Microsoft.AspNetCore.SpaServices, which is wired by default into the Vue template (as well as other SPA templates)!

I am referring to the Webpack dev middleware, which you can see being wired in the Startup.cs file:

if (env.IsDevelopment())
    app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions {
        HotModuleReplacement = true

What this does, is to intercept requests for files in ~/dist/ and automatically generate the bundles.

Even more interesting is the HotModuleReplacement option. With this option enabled, the webpack dev middleware will monitor changes to the TypeScript files, templates, css etc. of the client-side app. When any file changes, the bundles will be automatically regenerated and pushed to the browser which will reload only the code that changed!

For further reading, check the documentation on Microsoft’s middleware repo. You will find out that .NET middleware mostly delegates to webpack’s hot module replacement middleware which is used under the hood.

Webpack in production

When publishing your app to production, the aim is to produce bundles which are as optimized as possible.

This means developer tools like source maps won’t be used in the production bundles, but it also means additional plugins can be used to minify/compress the generated bundles. We have already seen these while going through the webpack config files.

So how is this production specific build invoked? The project comes with a publish task already wired that will basically run webpack in production mode and generate production optimized bundles in the wwwroot/dist folder.

You can see this if you open the .csproj file and look at the RunWebpack task:

<Target Name="RunWebpack" AfterTargets="ComputeFilesToPublish">
  <!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
  <Exec Command="npm install" />
  <Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js" />
  <Exec Command="node node_modules/webpack/bin/webpack.js" />

  <!-- Include the newly-built files in the publish output -->
  <!-- Removed for clarity -->

So, if you manually run dotnet publish -c Release on the command line or use the Publish menu of Visual Studio, webpack will be used to generate production optimized bundles and these will be included within the published files of your app.

Show me some code!

The template’s fetchdata page

Let’s start by taking a look at the component located in ClientApp/components/fetchdata. This is the Vue component that gets rendered when you hit the /fetchdata route as in http://localhost:5000/fetchdata.

The interesting part is that this component will automatically fetch some JSON data from the ASP.NET Core backend as soon as it gets rendered.

It does so by providing a function named mounted which sends an ajax request to fetch the data, and then updates the component’s data. The mounted function is one of the many lifecycle hooks provided by Vue and you don’t need to do anything special other than making sure your component has a function with the required name.

export default class FetchDataComponent extends Vue {
    forecasts: WeatherForecast[] = [];

    mounted() {
            .then(response => response.json() as Promise<WeatherForecast[]>)
            .then(data => {
                this.forecasts = data;

The fetch method is provided by the isomorphic-fetch library which is part of the vendor’s webpack bundle and adds that fetch function to the global scope.

All the component’s template needs to do is render each item of the forecast’s array, for example in a table:

<tr v-for="item in forecasts">
    <td>{{ item.dateFormatted }}</td>
    <td>{{ item.temperatureC }}</td>
    <td>{{ item.temperatureF }}</td>
    <td>{{ item.summary }}</td>

Given Vue’s reactive nature, as soon as the forecast array is updated in the component, the template is re-rendered. This way the items appear on the browser as soon as they were fetched from the server.

Hopefully you agree with me, this looks straightforward. Continue reading if you wish to create something of our own based on what we have discussed so far.

Adding a TODO list

Let’s implement the classical example of the TODO list, where we will add a new client-side route and components, backed by a REST API on the server side.

The API is the most straightforward and boring piece!

This is all very familiar to anyone who has created APIs either in ASP.NET Core or WebAPI. I have used in-memory storage to quickly have something up and running, but you could easily add a proper database storage.

public class TodoController : Controller
    private static ConcurrentBag<Todo> todos = new ConcurrentBag<Todo> {
        new Todo { Id = Guid.NewGuid(), Description = "Learn Vue" }

    public IEnumerable<Todo> GetTodos()
        return todos.Where(t => !t.Done);

    public Todo AddTodo([FromBody]Todo todo)
        todo.Id = Guid.NewGuid();
        todo.Done = false;
        return todo;

    public ActionResult CompleteTodo(Guid id)
        var todo = todos.SingleOrDefault(t => t.Id == id);
        if (todo == null) return NotFound();

        todo.Done = true;
        return StatusCode(204);
public class Todo
    public Guid Id { get; set; }
    public string Description { get; set; }
    public bool Done { get; set; }

With our API done, let’s add a new route and component to the client side.

Start by creating a new folder ClientApp/components/todos. Now let’s add a component file named todos.ts, with a new vue component:

import Vue from 'vue';
import { Component } from 'vue-property-decorator';

interface TodoItem {
    description: string;
    done: boolean;
    id: string;

export default class TodoComponent extends Vue {
    todos: TodoItem[];
    newItemDescription: string;

    data() {
        return {
            todos: [],
            newItemDescription: null

It doesn’t do much right now other than declaring a new component that will hold the list of todo items.

Let’s also add a new template file named todos.vue.html with the contents:

        <h1>Things I need to do</h1>
<script src="./todos.ts"></script>

Not very interesting right now, but let’s finish adding all the files and ensure we have everything wired correctly before adding the functionality.

Now add the new route in boot.ts adding the following entry to the routes array:

{ path: '/todos', component: require('./components/todos/todos.vue.html') }

Finally, open navmenu.vue.html and add another entry to the menu that renders the /todo route:

    <router-link to="/todos">
        <span class="glyphicon glyphicon-list-alt"></span> TODO list

Now run the project and you should see the entry in the menu rendering the todos component:


Figure 6, adding the todo route and component

Now that we have our todo component ready, let’s update it so it fetches the list of todos from the backend API and renders them in a list.

Simply add the mounted function to the component inside todos.ts and use the fetch method provided by isomorphic-fetch to load the todo items:

mounted() {
        .then(response => response.json() as Promise<TodoItem[]>)
        .then(data => {
            this.todos = data;

Then use vue’s v-for directive inside the template inside todos.vue.html to render each item as an <li> inside a list:

<ul v-if="todos.length">
    <li v-for="item in todos">{{item.description}}</li>
<p v-else class="text-success">Nothing to do right now!</p>

Didn’t work?

Double check if you have added that markup inside the outer <div> element which is the root of the template in todos.vue.html. Vue is very strict about having component’s template with a single root element.

Ok, we can now render all the items!

Let’s now add a button to complete each of these items. We can tweak the template, so for each item, we also add a button. This button will use vue’s v-on directive to call a method of the component when the click event of the html button is triggered:

<li v-for="item in todos">
    {{item.description}} – 
    <button v-on:click="completeItem(item)" class="btn btn-link">

The implementation of the completeItem method can be as simple as using the fetch method to send a DELETE request to the backend and remove the item from the list of todos:

completeItem(item: TodoItem){
    fetch(`/api/todo/${}`, {
        method: 'delete',
        headers: new Headers({
        'Accept': 'application/json',
        'Content-Type': 'application/json'
    .then(() => {
        this.todos = this.todos.filter((t) => !==;

The remaining task would be to create a form to add a new todo item. Let’s start by updating the template with a form:

    <div class="row">
        <div class="col-xs-6">
            <input v-model="newItemDescription" class="form-control" 
             placeholder="I just remembered that I have to...">
        <button v-on:click="addItem($event)" class="btn btn-primary">

The form has an input element whose value is bound to the newItemDescription property of the component’s data. When the click event of the button is triggered, the addItem method of the component is called.

The method will send a POST to the backend API and update the list of todos with the new item:

    if(event) event.preventDefault();
    fetch('/api/todo', {
        method: 'post',
        body: JSON.stringify(<TodoItem>{description: this.newItemDescription}),
        headers: new Headers({
        'Accept': 'application/json',
        'Content-Type': 'application/json'
    .then(response => response.json() as Promise<TodoItem>)
    .then((newItem) => {
        this.newItemDescription = null;

After all these changes, you should have a SPA with a fully functional (albeit simple) todo page:


Figure 7, completed todo page

This isn’t a comprehensive example, but it should be enough to get you started with Vue and the project template. After this, you should have an easier time exploring more advanced Vue functionality.


The modern JavaScript world is a complex and evolving one. It is hard to stay up-to-date and keep track of every new framework that appears and replaces something you just learned like grunt/gulp!

Thankfully, templates similar to the SPA ones provided by Microsoft, have already gone through the work of curating these tools and frameworks and have provide you with a very decent and up-to-date starting point. With these ASP.NET Core SPA templates, you can start adding value instead of spending a week understanding and configuring today’s hot libraries.

Of course, at some point, you will need to understand these pieces, but you can do so gradually and it doesn’t act as a barrier preventing you from starting a new project on the right track.

And Vue is a great option as a client-side framework for your next project. It strikes a lovely balance between simplicity, performance and power that makes it easy to get started, but gradually supports more advanced features and applications.

I understand choosing a framework is not an easy task and there are a lot of factors to consider like team skills or legacy code, but I personally would at least consider Vue even if I had to later discard it because of some valid team/company/project reasons.

I would love to hear about your experiences working with Vue and ASP.NET Core SPA templates.

Download the entire source code from GitHub at

Was this article worth reading? Share it with fellow developers too. Thanks!
Share on LinkedIn
Share on Google+
Further Reading - Articles You May Like!
Daniel Jimenez Garcia is a passionate software developer with 10+ years of experience. He started as a Microsoft developer and learned to love C# in general and ASP.NET MVC in particular. In the latter half of his career he worked on a broader set of technologies and platforms while these days he is particularly interested in .Net Core and Node.js. He is always looking for better practices and can be seen answering questions on Stack Overflow.

Page copy protected against web site content infringement 	by Copyscape

Feedback - Leave us some adulation, criticism and everything in between!