The sky is the limit! The idea is to have everything available. Since Blazor is relatively new, there aren’t many packages, but don’t worry. I will show you how to include NPM packages with your Blazor Hybrid app and make everything bulletproof by adding TypeScript. Since Microsoft does not natively support .ts or .js files, we’ll include that support. Microsoft will be jealous! 😄
Requirement
- For the purpose of this tutorial let's clone this repo branch
Setup
-
Navigate to
MyBlazorHybridAppand create a new directory namednpm. -
Open a new terminal in that directory or
cdinto it. -
Create a
package.jsonfile:npm init -y -
Install necessary packages:
npm install typescript ts-loader webpack webpack-cli webpack-dev-server babel-loader @babel/core @babel/preset-env --save-dev -
Initialize a TypeScript configuration file:
npx tsc --init -
Create a new file named
webpack.config.jsand add the following content:const path = require('path'); const fs = require('fs'); function findFiles(dir, extensions, fileList = []) { const files = fs.readdirSync(dir); files.forEach((file) => { const fullPath = path.join(dir, file); const stat = fs.statSync(fullPath); if (stat.isDirectory()) { findFiles(fullPath, extensions, fileList); } else if (extensions.includes(path.extname(file).toLowerCase())) { fileList.push(fullPath); } }); return fileList; } module.exports = (env, argv) => { const mode = argv.mode || 'development'; const entry = [ ...findFiles(path.resolve(__dirname, '../Views'), ['.ts', '.js', '.razor.ts', '.razor.js']), './src/index.ts', ]; return { mode, devtool: mode === 'development' ? 'source-map' : false, entry, output: { filename: 'index.bundle.js', path: path.resolve(__dirname, '../wwwroot/js'), }, module: { rules: [ { test: /\.ts$/, use: 'ts-loader', exclude: /node_modules/, }, { test: /\.js$/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] }, }, exclude: /node_modules/, }, ], }, resolve: { extensions: ['.ts', '.js'] }, devServer: { static: path.resolve(__dirname, '../wwwroot'), compress: true, port: 9000, }, }; }; -
Create a
srcdirectory inside thenpmfolder and add anindex.tsfile with the following content:console.log("Hello from TypeScript"); -
Update the
tsconfig.jsonfile with the following:{ "compilerOptions": { "target": "es6", "module": "es6", "strict": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "skipLibCheck": true, "sourceMap": true }, "include": ["src/**/*.ts"], "exclude": ["node_modules"] } -
Add the following scripts to
package.json:"scripts": { "build": "webpack --mode production", "build-dev": "webpack --mode development", "debug": "webpack serve" },
Consuming the Script
-
Add the
index.bundle.jsscript to your app:- For
BlazorHybridApp.Web, add the following towwwroot/index.html:<script src="_content/MyBlazorHybridApp/js/index.bundle.js"></script> - For
BlazorHybridApp.Maui, add the same script towwwroot/index.html.
- For
-
Modify the
MyBlazorHybridApp.csprojfile to automate the build process:<Target Name="NpmBuild" AfterTargets="Compile"> <Exec Command="npm install" WorkingDirectory="npm" /> <Exec Command="npm run build-dev" WorkingDirectory="npm" Condition="'$(Configuration)' == 'Debug'" /> <Exec Command="npm run build" WorkingDirectory="npm" Condition="'$(Configuration)' == 'Release'" /> </Target>
Testing
-
Add a new file named
Home.razor.jsin theViewsdirectory with the following content:// Show alert message window.showAlert = (message) => { alert(message); }; -
Update the
home.razor.csfile:using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; namespace MyBlazorHybridApp.Views; public partial class Home { [Inject] private IJSRuntime JS { get; set; } = default!; private async Task TriggerAlert() { // Call JavaScript function from Blazor await JS.InvokeVoidAsync("showAlert", "Button clicked from Razor component!"); } } -
Add a button in
home.razorto trigger the alert:<button @onclick="TriggerAlert">Click Me</button>
Run your app and test the functionality. If everything was set up correctly, clicking the button should display an alert.