Including Static Resources In Razor Class Libraries In ASP.NET Core

Razor Class libraries (RCLs) were introduced in ASP.NET Core 2.1 as a way to package and distribute UI components to be referenced and consumed within a host application. You can see them as a fully functioning part of a web application packaged as a class libray. The templated management pages for ASP.NET Identity were included as a Razor Class Library in the project template in 2.1. The Identity UI RCL includes the Razor pages, Filters, a stub EmailService class that Identity uses, and local versions of both Bootstrap 3 and 4.

Most examples that show the creation of a Razor Class Library that I have seen don't include guidance on incorporating static resources like Bootstrap or custom JavaScript files. It's not a particularly intuitive process or well documented. My example will demonstrate the steps required to do this. The example itself will show the starting point of creating a headless CMS - a perfect candidate for a Razor Class Library. The example won't include any CMS functionality. That would only obscure the purpose of this article.

Creating the Class Library

  1. Start a new project in Visual Studio choosing the New ASP.NET Core Web Application template and name it EditorRCL
  2. Select the Razor Class Library option, ensuring that the version is ASP.NET Core 2.1 or higher
    Razor Class Library

    Razor Class Library
  3. Build (or wait for VS to perform a restore) to get rid of warnings and then rename the MyFeature folder to Editor and delete Page1.cshtml and its PageModel class file
  4. Add a Razor Page named Index.cshtml to the Pages folder in the Editor area with the following code:


  5. Right click on the solution in Soultion Explorer and choose Add » New Project, and select ASP.NET Core Web Application. Name it EditorHost and then choose Web Application. The Razor Pages application that gets generated will provide the test environment for the Razor class library.
  6. Add a reference to the EditorRCL from within the EditorHost site. You can do this by right-clicking on EditorHost project in Solution Explorer and choosing Add » Reference, or by modifying the EditorHost.csproj file to include a ProjectReference entry so that it looks like this:
  7. Make sure that EditorHost is set as the startup project

    and run the application. You should get the default template home page. Add /editor to the URL. The result should look like this:


    If it does, then the Razor class library has been referenced correctly and is working.

However, the output is totally unstyled. If you look at the source for the page, it consists of a single line of HTML: <h1>RCL Editor</h1>. The page needs a Layout.

Adding a Layout

  1. Add a new folder named Shared to the Pages folder in the EditorRCL project.
  2. Add a Razor Layout named _Layout.cshtml to this folder with the following code:

    This is a slightly modified version of the template generated for Razor Pages 2.2 applications. It references Bootstrap 4 and a Google Icon pack. It has most of the navigation section removed.
  3. Add a new Razor View Start file to the Pages folder in the EditorRCL project named _ViewStart.cshtml. The default template should contain the following code:

    Now if you run the application, it looks like the Layout page is working:

    Razor Class Library

    And it is working - to an extent. It is being referenced correctly from the Razor Page, but if you look at the source code, you can see environment tags and other tag helper attributes:

    Razor Class Library

    The Tag Helpers are not being processed. They look like they might be, because Bootstrap is being applied. However the CSS file that should be used in development mode is supposed to be located in a folder named css, which doesn't exist yet. The rendered environment tags are being ignored by the browser and the CDN version of Bootstrap is being used instead.

  4. Tag helpers are an opt-in feature, so we shall opt in. Add a new Razor View Imports file to the Pages folder in the EditorRCL project. Add the following code to the file:

    Now when you run the page, you will find that no Bootstrap styles are being applied, and the browser cannot find any css or js files being referenced in Development mode:

    Razor Class Library

Adding Static Resources

The final step involves adding local copies of script and style files to the class library and then configuring them to work.

  1. Create a folder named resources in the root of the EditorRCL project (at the same level as the Areas folder) ands within that add two further folders named css and js:
    Razor Class Library
  2. Add Bootstrap 4 and Jquery files to the folders. I copied these across from a Razor Pages 2.2 project template (created using the dotnet new razor command with the preview of .NET Core 2.2 SDK installed). Alternatively you can obtain them yourself using npm, Nuget or by going to the project sites and obtaining the files. You should also create a site.css file and a site.js file and add those to the relevant folders:
    Razor Class Library
  3. Static files in a standalone class library cannot be browsed like those in a web application. They need to be included in the resulting compiled assembly as embedded resources. So the next step is to alter the EditorRCL.csproj file to specify that the contents of the resources folder should be included as an embedded resource (lines 14-16), include the Microsoft.Extensions.FileProviders.Embedded package (line 10) and Microsoft.AspNetCore.StaticFiles (line 100), and importantly, to specify that a manifest is generated for the embedded resources (line 5):
  4. Next, borrowing code from the IdentityUI source, add the following class to the EditorRCL project: This code creates an additional FileProvider, pointing to the resources folder, and adds it to those that retrieve static files.
  5. Add the following extension method to simplify applying the configuration in the Startup class ConfigureService method:
  6. Modify the ConfigureServices method in the EditorHost application's Startup class to include a call to the extension method above (line 10 below): Now when you run the application, you can see that the embedded resources are retrieved successfully:
    Razor Class Library

Summary

Getting static files to work in a Razor Class Library isn't particularly difficult, once you know how. The trick is to remember that they are embedded resources. That's why I don't name the folder that I place them in wwwroot, or anything similar that could confuse the issue. There is an open issue to improve this experience, but it looks like it's been kicked into the long grass. So in the meantime, the steps above should help you if you are trying to author a Razor Class Library that relies on its own static resources.