An example .NET 8 Blazor Web project working with Vite build tooling
The .NET workflow with Vite is outstanding. It provides a much more modern and faster development experience than when using Webpack. If you're interested in how to set up a new .NET project with Blazor and Vite, please follow the steps below and use this repository as a reference.
- Run
npm init -yto scaffold the defaultpackage.jsonat the root of the Web project. npm install vite --save-devnpm install vite-plugin-mkcert --save-devnpm install sass@npm:sass-embedded@latest --save-devNote that this is currently using an NPM alias for 'sass', but Vite intends to support the 'sass-embedded' package directly in the future as it is much faster. See: vitejs/vite#6734
npm install bootstrap --save
- Run
dotnet add package Vite.AspNetCoreat the root of the Web project.
-
Using your desired text editor (hint: you can use
code .to open VS Code at the current directory), open the directory and create avite.config.jsfile at the root of the Web project. -
Save the following configuration into this newly created file:
import { defineConfig } from "vite"; import mkcert from "vite-plugin-mkcert"; import path from "path"; import { Vite } from "./appsettings.Development.json"; export default defineConfig({ appType: "custom", root: "Client", plugins: [ mkcert() ], resolve: { alias: { "~": __dirname } }, build: { manifest: true, emptyOutDir: true, outDir: path.join(__dirname, "wwwroot"), assetsDir: "", rollupOptions: { input: [ path.join(__dirname, "Client", "scripts", "app.js"), path.join(__dirname, "Client", "styles", "app.scss") ], output: { entryFileNames: "js/[name]-[hash].bundle.min.js", chunkFileNames: "js/[name]-[hash].chunk.min.js", assetFileNames: (info) => { if (info.name) { // If the file is a CSS file, save it to the "css" folder if (/\.css$/.test(info.name)) { return "css/[name]-[hash].bundle.min.[ext]"; } // If the file is an image file, save it to the "img" folder if (/\.(png|jpe?g|gif|svg|webp|avif)$/.test(info.name)) { return "img/[name][extname]"; } // If the file is any other type of file, save it to the "assets" folder return "assets/[name][extname]"; } else { // If the file name is not specified, save it to the output directory return "[name][extname]"; } }, manualChunks: (id) => { if (id.includes('node_modules')) { return 'lib'; } } } } }, server: { port: Vite.Server.Port, strictPort: true, hmr: { host: "localhost", clientPort: Vite.Server.Port } } });
-
Add the following 'Vite' section to the
appsettings.Development.jsonfile, so that it looks like the below:{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "Vite": { "Server": { "Enabled": true, "Port": 5173, "Https": true, "AutoRun": true } } } -
Create a new directory at the project root called 'Client' e.g.
mkdir Client. -
Inside there, create 3 more directories to be used as input for the Vite build:
public,scriptsandstyles. -
Lastly create an
app.jsfile inside ofClient/scripts/and anapp.scssfile inside ofClient/styles/to be used for the Javascript and CSS respectively. Note that the 'public' directory will be used for static assets such as the favicon that need to be copied to the output with no compilation or build process applied.
We already installed the Vite.AspNetCore package earlier which does most of the heavy lifting, but there is a final piece of configuration needed for everything to work.
-
Add the following NPM scripts to the
package.jsonfile:"start": "echo Starting the development server && vite", "dev": "vite", "build": "vite build --mode development", "publish": "vite build --mode production"
-
Open the
wwwrootdirectory and copy the contents ofapp.cssinto our newapp.scssfile. -
Delete the default bootstrap folder and add the line
@import "bootstrap/scss/bootstrap";to the top ofapp.scss. -
Copy the
favicon.pngto theClient/public/directory. -
Add the following to the top of the
Program.csfile:using Vite.AspNetCore; //...omitted for brevity // Add the Vite manifest integration. builder.Services.AddViteServices();
-
Add the following to the bottom of the
Program.csfile:if (app.Environment.IsDevelopment()) { if (bool.Parse(builder.Configuration["Vite:Server:Enabled"] ?? string.Empty)) { // Proxies requests for css and js to // the Vite development server for HMR. app.UseViteDevelopmentServer(true); } }
-
Finally, update the
App.razorcomponent to use the new script and style assets provided via Vite by placing the following within the<head>tags:@using Vite.AspNetCore @inject IWebHostEnvironment env @inject IViteManifest manifest @inject IViteDevServerStatus vite //...omitted for brevity @if (env.IsDevelopment() && vite.IsMiddlewareEnable) { <script type="module" src="@@vite/client"></script> <script type="module" src="scripts/app.js" defer></script> /** Note that this .scss is correct as it's purely acting as a 'key' to the real css file being held in the Vite server memory. **/ <link rel="stylesheet" href="styles/app.scss" /> } else { IViteChunk? js = manifest["scripts/app.js"]; if (js is not null) { <script type="module" src="@js.File" defer></script> if (js.Imports is not null) { foreach (string import in js.Imports) { if (manifest.ContainsKey(import) is false) continue; <script type="module" src="@manifest[import]!.File" defer></script> } } } IViteChunk? css = manifest["styles/app.scss"]; if (css is not null) { <link rel="stylesheet" href="@css.File" /> } }
-
Test by running
npm run buildto ensure that Vite successfully built and output the js and css files into thewwwrootdirectory. -
You can now run
dotnet watch runto run the application and see the Hot Reload in action after making a change to the scripts or styles.