Publishing a self-contained Blazor component (Razor + CSS + JS) as a NuGet package
In this post, I describe how to create and publish a self-contained Blazor component as a NuGet package. By self-contained I mean that the NuGet package will contain the Blazor component (Razor + C#), the style (CSS) and the JavaScript code. Also, the consumer of the package only has to use the component without any additional steps such as manually including the CSS or JS files.
@* The CSS and the JS are automatically included *@
<MyRazorClassLibrary.SayHello />
To create the component I'll use the following features of .NET 5:
- CSS isolation to create a scoped style
- JS isolation to load the JavaScript code when needed
If these features are new to you, you should start by reading the previous links and continue on this post after.
#Creating the library
First, you need to create a new project. You can use the "Razor Class Library" template in Visual Studio or you can use the command line:
dotnet new razorclasslib --name MyRazorClassLibrary
The generated csproj file should look like:
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<SupportedPlatform Include="browser" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="5.0.0-rc.2.20475.17" />
</ItemGroup>
</Project>
For the component I'll reuse the component from my previous post about JS isolation. Let's start with the JS file. You can create a new JS file named wwwroot/demo.js
. The files under the wwwroot
folder are automatically embedded in the DLL when compiling and are accessible using the URL _content/<ProjectName>/<FileName>
.
// wwwroot/demo.js
export function sayHi(name) {
alert(`hello ${name}!`);
}
Let's create a new file named SayHello.razor
. This component use the JSRuntime
and JSObjectReference
object to import the JS file dynamically and use its methods as explained in the JS isolation post. Please note that you need to change the value of ImportPath
to match your library name.
@using Microsoft.JSInterop
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Forms
@inject IJSRuntime JSRuntime
@implements IAsyncDisposable
Enter your name:
<input @bind="name" />
<button @onclick="Submit">Submit</button>
@code {
private string name;
private Task<IJSObjectReference> _module;
@* TODO: You need to replace MyRazorClassLibrary with the actual name of your library *@
const string ImportPath = "./_content/MyRazorClassLibrary/demo.js";
private Task<IJSObjectReference> Module => _module ??= JSRuntime.InvokeAsync<IJSObjectReference>("import", ImportPath).AsTask();
async Task Submit()
{
var module = await Module;
await module.InvokeVoidAsync("sayHi", name);
}
public async ValueTask DisposeAsync()
{
if (_module != null)
{
var module = await _module;
await module.DisposeAsync();
}
}
}
Finally, you need to define the style of the component. We'll use the CSS isolation feature to create a style that only applies to this component. So, when you include this component in another project, you are sure your style is applied. Let's create a file named SayHello.razor.css
with the following content:
button {
color: green;
}
Your project should look like:
#Publishing the NuGet package
The component is now ready to be published as a NuGet package. Let's use the dotnet
command line to package the component and publish it!
.NET 5.0 RC1 only: Edit the csproj to exclude the isolated CSS files from the NuGet package (this is fixed in RC2)
csproj (MSBuild project file)<!-- Only on .NET 5.0 RC1, not needed with .NET 5.0 RC2 --> <ItemGroup> <None Update="**/*.razor.css" Pack="false" /> </ItemGroup>
Build the NuGet package using
dotnet publish
. This will output the NuGet package to thebin/Release
folder.Shelldotnet pack MyRazorClassLibrary.csproj --configuration Release
Publish the NuGet package to nuget.org. You first need to create an API key on nuget.org.
Shelldotnet nuget push bin/Release/MyRazorClassLibrary.1.0.0.nupkg --api-key "<your api key>" --source https://api.nuget.org/v3/index.json
If everything's ok, your package is available on nuget.org:
#Consuming the library
You can now consume your library into a Blazor application. First, add a reference to the NuGet package:
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="5.0.0-rc.2.20475.17" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="5.0.0-rc.2.20475.17" PrivateAssets="all" />
<PackageReference Include="System.Net.Http.Json" Version="5.0.0-rc.2.20475.5" />
<PackageReference Include="MyRazorClassLibrary" Version="1.0.2" />
</ItemGroup>
</Project>
Then, you can use the component from the NuGet package in your application:
@page "/"
<h1>Hello, world!</h1>
Welcome to your new app.
<MyRazorClassLibrary.SayHello />
When you run the application, you should see the component. The button text should be green as defined in the scoped CSS, and the JS file should be loaded when you click on the button to show the alert box:
#Additional Resources
- CSS Isolation
- JS Isolation
- Continuously publishing Nuget package to MyGet using VSTS
- Quickstart: Create and publish a package (dotnet CLI)
Do you have a question or a suggestion about this post? Contact me!