I Was Wrong - Hosting SPAs in ASP.NET Core
Tags: aspnetcore, angular, vue, spas
Published: 2021-02-15 09:19:37

---
title: I Was Wrong - Hosting SPAs in ASP.NET Core
published: true
date: 2021-02-15 02:48:37 UTC
tags: ASPNETCore,Angular,Vue,SPAs
canonical_url: http://wildermuth.com/2021/02/15/I-Was-Wrong---Hosting-SPAs-in-ASP-NET-Core
cover_image: https://wilderminds.blob.core.windows.net/img/I-Was-Wrong--Hosting-SPAs-in-ASP.NET-Core/client-side-cover.jpg
---

If you've seen my projects or courses over the last few years, I had been advocating for using NPM for client-side libraries and SPAs at the same time. This caused the need to merge generated **packages.json** files. This added to the complexity. In addition, to handle the client-side libraries with NPM, you needed a way to both read the **node\_modules** folder during development. This was handled with some [middle-ware](https://github.com/OdeToCode/UseNodeModules) created by the late, great @OdeToCode (K. Scott Allen).

As I'm updating some of my [Pluralsight](https://shawnl.ink/psauthor) courses, I'm changing them to use this simpler approach. Here's what I am proposing instead.

### Client Side Libraries

The old advice of using NPM to define client-side libraries made sense to me, but it came with some drawbacks:

- Visual Studio/VS Code had a hard time with intellisense of the **node\_modules** folder.
- You needed an additional step (usually introducing Gulp) for publishing your project. It was one more technology to stay up to date on.
- The number of files needed was almost always way more than necessary.
- Going from NPM to CDNs wasn't simple enough.

I think I prefer now to use simple management of these libraries. I've realized that for many projects, the number of these files is pretty small and the multiple moving pieces wasn't necessary just to use a couple of libraries.

Instead, I'm recommending one of three approaches:

- If you're using Visual Studio, Library Manager simplifies including these libraries.
- Use CDNs instead of including the libraries.
- Manually include them in your project.

Library Manager supports both Visual Studio integration and the **libman** CLI tool. If you're using Visual Studio, you can get started by just right-clicking on the project and selecting "Manage Client-Side Libraries...":

![Client-Side Libraries](https://wilderminds.blob.core.windows.net/img/I-Was-Wrong--Hosting-SPAs-in-ASP.NET-Core/client-side-lib.jpg)

Once you do that it'll create a new project called "libman.json":

```
{
  "version": "1.0",
  "defaultProvider": "cdnjs",
  "libraries": [
  ]
}

```

While you can manage this file manually, I'd use the tooling to do it instead. For example, to add a new library, you can right-click the project and pick **Add->Client-Side Library** :

![Add new Client-Side Library](https://wilderminds.blob.core.windows.net/img/I-Was-Wrong--Hosting-SPAs-in-ASP.NET-Core/client-side-lib-2.jpg)

This will bring up a dialog that allows you to specify the library:

![Adding JQuery](https://wilderminds.blob.core.windows.net/img/I-Was-Wrong--Hosting-SPAs-in-ASP.NET-Core/client-side-dialog.jpg)

Note, you can specify the provider (I prefer unpkg, but it doesn't matter which one) and type in the library name. You'll see a dropdown of possible libraries and once you tab out of the Library, it'll add the version number of the latest version (you can specify the version number with an **@** symbol):

![Package options](https://wilderminds.blob.core.windows.net/img/I-Was-Wrong--Hosting-SPAs-in-ASP.NET-Core/client-side-package.jpg)

Additionally, you can pick what files to include (I usually just get the 'dist' folder of packages). Lastly, I pick where I want to install it, but by default it picks a **lib** folder under **wwwroot**.

Library Manager can refresh the libraries or upgrade them as well. But this way it's pretty simple to manage the files for development or production.

But SPAs are a bit different.

### Using SPAs

In my new thinking about this, I usually plan on doing a SPA (e.g. Angular, Vue, React) in a sub-folder of the project. In my example, I do this in **ng-cient** and **vue-client** folders. The idea is to tell the SPA project to build directly into the **wwwroot** folder. To show you how this works, let's walk through it with Angular and Vue.js.

#### Angular

In angular, the result of the building of the project is normally built in a subdirectory of the Angular project called **dist**. You can find where this is set via the angular.json file:

![Angular Configuration](https://wilderminds.blob.core.windows.net/img/I-Was-Wrong--Hosting-SPAs-in-ASP.NET-Core/angular-config.jpg)

The trick here is to change this to be inside the **wwwroot** directory:

```
"outputPath": "..\\wwwroot\\ng-client",

```

In this case, the Angular project is a subdirectory of the ASP.NET Core project. By specifying one folder down and then up to wwwroot, I can create the result of the Angular build to be accessible from the ASP.NET Core project. You end up with the files in **wwwroot** :

![](https://wilderminds.blob.core.windows.net/img/I-Was-Wrong--Hosting-SPAs-in-ASP.NET-Core/ng-client-in-wwwroot.jpg)

Once you do this, you can now just use relative links to this new directory structure:

```
@page
@section Scripts {
  <script src="~/ng-client/runtime.js"></script>
  <script src="~/ng-client/polyfills.js"></script>
  <script src="~/ng-client/vendor.js"></script>
  <script src="~/ng-client/main.js"></script>
}
@section Head {
  <link href="~/ng-client/styles.css" rel="stylesheet" />
}
<div>
  <app-root></app-root>
</div>

```

We can do the same in most SPA frameworks. Let's see how it works in Vue.

#### Vue

For Vue it's a little bit more complicated as the configuration file doesn't exist by default. You should just create a new file in the Vue project folder called **vue.config.js** :

![](https://wilderminds.blob.core.windows.net/img/I-Was-Wrong--Hosting-SPAs-in-ASP.NET-Core/vue-config-file.jpg)

Once you create this, you'll want to export an object with properties of outputDir (for the output folder) and turn off filenameHashing to prevent the production version from having hashing:

```
// vue.config.js
module.exports = {
  outputDir: "..\\wwwroot\\vue-client",
  filenameHashing: false
}

```

When you build the project, you should see the files now created it it's own directory:

![](https://wilderminds.blob.core.windows.net/img/I-Was-Wrong--Hosting-SPAs-in-ASP.NET-Core/vue-wwwroot.jpg)

Then you can use it like any other project:

```
@page
@section Scripts {
  <script src="~/vue-client/js/chunk-vendors.js"></script>
  <script src="~/vue-client/js/app.js"></script>
}
<div>
  <div id="app"></div>
</div>

```

This is similar with most SPA frameworks.

## Conclusion

You can get an example from Github here:

> [https://github.com/shawnwildermuth/spas-in-aspnetcore](https://github.com/shawnwildermuth/spas-in-aspnetcore)

I hope this helps you see how I was wrong and maybe improve your process, especially if you learned from my courses.

[![Creative Commons License](http://i.creativecommons.org/l/by-nc-nd/3.0/88x31.png)](http://creativecommons.org/licenses/by-nc-nd/3.0/)

 This work by [Shawn Wildermuth](http://wildermuth.com) is licensed under a [Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License](http://creativecommons.org/licenses/by-nc-nd/3.0/).  
 Based on a work at [wildermuth.com](http://wildermuth.com).

* * *

If you liked this article, see Shawn's courses on [Pluralsight](http://shawnw.me/pscourses).
 ![](http://feeds.feedburner.com/~r/ShawnWildermuth/~4/ZfFcsY6Ht80)