
Riprendiamo a parlare di aspetti avanzati di Blazor, affrontando uno degli strumenti più interessanti che abbiamo a disposizione per il riutilizzo del codice che scriviamo. Abbiamo già visto negli articoli precedenti che possiamo creare un libreria di classi per condividere oggetti di scambio tra back-end e front-end, in questo articolo vedremo invece come poter condividere i componenti che realizziamo per la nostra interfaccia, in modo da poterli riutilizzare in applicazioni diverse.
Creazione della libreria
Partiamo dalla creazione di una cartella LibreriaComponenti
, all’interno della quale, da riga di comando, creaiamo una solution vuota, aggiungiamo un progetto Blazor WASM per testare la nostra libreria e la aggiungiamo alla solution:
mkdir LibreriaComponenti
cd LibreriaComponenti
dotnet new sln
dotnet new blazorwasm -o LibreriaComponenti.Host.BlazorWASM
dotnet sln add ./LibreriaComponenti.Host.BlazorWASM
Se lanciate il comando dotnet new
, la CLI vi mostrerà i template disponibili, tra i quali ne troverete uno di cui probabilmente non vi siete mai accorti: razorclasslib
. Si tratta di una libreria di classi pensata per Razor Pages, su cui come sappiamo si basa il framework Blazor. Questo template è proprio quello di cui abbiamo bisogno per i nostri scopi, non ci resta quindi che creare la libreria, aggiungerla alla solution e creare un riferimento ad essa dal progetto Blazor WASM:
dotnet new razorclasslib -o LibreriaComponenti
dotnet sln add ./LibreriaComponenti
cd LibreriaComponenti.Host.BlazorWASM
dotnet add reference ../LibreriaComponenti
Ovviamente potremmo fare tutto questo da Visual Studio con i classici wizard, ma la riga di comando, oltre ad avere sempre il suo fascino, è cross-platform! Apriamo il progetto con Visual Studio Code (o Visual Studio) e diamo una occhiata alla struttura generata:

Il template crea per noi un componente di esempio denominato Component1
e aggiunge anche la cartella wwwroot per contenere gli eventuali elementi statici a corredo dei nostri componenti. Nel file ExampleJsInterop.cs
possiamo anche notare un metodo che richiama una funzione JavaScript presente nel file exampleJSInterop.js
, come esempio di interoperabilità tra .NET e JavaScript (potete approfondire qui e qui).
Usiamo la libreria
Possiamo quindi aggiungere al file _Imports.razor
del progetto Host la using alla libreria (@using LibreriaComponenti
) e utilizzare il componente di esempio nella pagina Index.razor
:
@page "/"
<Component1></Component1>
ed ecco il risultato:

Da dove origina il bordo tratteggiato in rosso del componente? A partire dalla versione 5 di Blazor è stata introdotta la possibilità di isolare gli stili CSS a livello di componente e pagina per evitare conflitti di stili in componenti innestati e dipendenze da stili globali che possono essere complesse da gestire. Per il nostro componente Component1 basta creare un file che abbia il nome Component1.razor.css nella stessa cartella.
.my-component {
border: 2px dashed red;
padding: 1em;
margin: 1em 0;
background-image: url('background.png');
}
Questa classe viene applicata solo all’output renderizzato del componente Component1. Ok ma il codice della nostra single page non referenzia questo file css. Come fa a funzionare? A livello di build, Blazor riscrive gli stili e produce un asset statico che viene referenziato nel tag head di wwwroot/index.html oppure in Pages/_Layout.cshtml.
<link href="LibreriaComponenti.Host.BlazorWASM.styles.css" rel="stylesheet" />
Proviamo a modificare il componente aggiungendo un pulsante che invoca la funzione JavaScript. Aggiungiamo al file _Imports.razor
della libreria il riferimento alla libreria JSInterop
:
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.JSInterop
In questo modo possiamo iniettare il JSRuntime
nel componente e usarlo sul pulsante:
@inject IJSRuntime JSRuntime
<div class="my-component">
This component is defined in the <strong>LibreriaComponenti</strong> library.
<button @onclick="ShowPrompt">
Chiama funzione JavaScript
</button>
</div>
@code {
public async Task ShowPrompt()
{
var interop = new ExampleJsInterop(JSRuntime);
await interop.Prompt("Come ti chiami?");
}
}
Ecco il risultato:

Anche in questo caso stiamo utilizzando delle funzionalità di Blazor offerte a partire dalla versione 5 ossia l’isolamento del codice JavaScript in moduli standard. Gli immediati vantaggi sono due: il codice JavaScript importato non ingolfa il namespace globale ma soprattutto chi vuole consumare una libreria non deve più importare i file JS creati. Se osserviamo il file wwwroot/exampleJsInterop.js troveremo il seguente codice
// This is a JavaScript module that is loaded on demand. It can export any number of
// functions, and may import other JavaScript modules if required.
export function showPrompt(message) {
return prompt(message, 'Type anything here');
}
Nel codice del costruttore della classe ExampleJsInterop viene creata una istanza della classe Lazy<Task<IJSObjectReference>> che è una referenza al modulo JavaScript importato
moduleTask = new (() => jsRuntime.InvokeAsync<IJSObjectReference>(
"import", "./_content/LibreriaComponenti/exampleJsInterop.js").AsTask());
Nel metodo Prompt della classe ExampleJsInterop viene invocato il codice JavaScript mediante il moduleTask precedentemente costruito
public async ValueTask<string> Prompt(string message)
{
var module = await moduleTask.Value;
return await module.InvokeAsync<string>("showPrompt", message);
}
Il file JavaScript non viene caricato nella nostra single page ma se andiamo a spiare l’attività di rete in corrispondenza del click del bottone troveremo l’importazione del nostro modulo JavaScript

Usare una libreria con Blazor WASM e Blazor WebAssembly
Un uso interessante delle librerie di componenti in questo periodo di attesa del rilascio di Blazor WASM, è l’uso di un progetto condiviso tra un host WebAssembly e un host Blazor Server. Uno scenario del genere, oltre a permetterci di debuggare comodamente in Blazor Server ed eseguire gli stessi componenti in Blazor WASM, dove il debug è ancora ostico, ci permette anche di evidenziare i punti differenzianti tra i due modelli di hosting.
Copiamo le cartelle Pages
e Shared
dal progetto WASM al progetto della libreria, aggiungendo alle dipendenze del progetto la libreria System.Net.Http.Json
. Il file LibreriaComponenti.csproj
diventa il seguente:
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<SupportedPlatform Include="browser" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components" Version="6.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="6.0.1" />
<PackageReference Include="System.Net.Http.Json" Version="6.0.0" />
</ItemGroup>
</Project>
Modifichiamo anche il file _Imports.razor
della libreria aggiungendo i namespace necessari:
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.JSInterop
@using System.Net.Http
@using System.Net.Http.Json
@using LibreriaComponenti.Shared
Spostiamo anche il componente App.razor
nella libreria, modificando la configurazione del Router per caricare le pagine dall’assembly della libreria, modificando il valore dell’attributo AppAssembly
da "@typeof(Program).Assembly"
a "@typeof(MainLayout).Assembly"
.
<Router AppAssembly="@typeof(MainLayout).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
Rilanciando il progetto vedrete che tutto continua a funzionare correttamente. Creiamo adesso un progetto Blazor Server, aggiungiamolo alla solution e aggiungiamo la reference alla libreria condivisa:
dotnet new blazorserver -o LibreriaComponenti.Host.BlazorServer
dotnet sln add ./LibreriaComponenti.Host.BlazorServer
cd LibreriaComponenti.Host.BlazorServer
dotnet add reference ../LibreriaComponenti
Eliminiamo la cartella Shared e i file nella cartella Pages a meno del file _Host.cshtml e _Layout.cshtml
nonché il file App.razor. Ricordiamoci di aggiungere al file _Imports.razor
il namespace della libreria (@using LibreriaComponenti
). Notiamo di nuovo come non sia necessario aggiungere riferimenti a file statici. Avviando il progetto server vedrete che apparentemente tutto funziona… Trovate il codice illustrato sull’account GitHub della community.
Conclusioni
Abbiamo visto come integrare una libreria di componenti in progetti Blazor Server e Blazor WebAssembly, utilizzando i template messi a disposizione da Microsoft. Abbiamo anche visto come sfruttare una libreria per condividere il codice tra i due modelli di hosting messi a disposizione dal framework, ma non tutto sta funzionando correttamente come potrebbe sembrare. Infatti se provate ad accedere alla pagina FetchData
, questa non funzionerà correttamente in Blazor Server a causa dell’uso del client HTTP. Nel prossimo articolo vedremo come risolvere questo problema sfruttando la dependency injection di .NET Core.