Publishing a self-contained Blazor component (Razor + CSS + JS) as a NuGet package

  • Gérald Barré

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:

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!

  1. .NET 5.0 RC1 only: Edit the csproj to exclude the isolated CSS files from the NuGet package (this is fixed in RC2)

    <!-- Only on .NET 5.0 RC1, not needed with .NET 5.0 RC2 -->
    <ItemGroup>
        <None Update="**/*.razor.css" Pack="false" />
    </ItemGroup>
  2. Build the NuGet package using dotnet publish. This will output the NuGet package to the bin/Release folder.

    dotnet pack MyRazorClassLibrary.csproj --configuration Release
  3. Publish the NuGet package to nuget.org. You first need to create an API key on nuget.org.

    dotnet nuget push bin/Release/MyRazorClassLibrary.1.0.0.nupkg --apikey "<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

Do you have a question or a suggestion about this post? Contact me!

Follow me:
Enjoy this blog?Buy Me A Coffee