Fluent Design with Blazor; What are your options?
Update
If you are curious to the lastest state of affairs (as of November 2021) on working with Fluent UI in Blazor, be sure to visit the follow-up to this article, Revisited: Fluent Design with Blazor, once you're done reading this.
The standard design you get out of the box when creating a Blazor application is based on Bootstrap. It doesn't matter whether you go for a server side or a web assembly implementation. But what do you need to do if you would like to use a different design? Say, for example, dress up your app in the Microsoft Office 365 look. This post shows you your options to achieve exactly that. If you are not yet familiar with Blazor and/or Fluent Design, you're in luck, because I'll start with a (brief) explanation of both first. This gives us the foundation to build upon.
What is Blazor?
Blazor is a framework for building interactive web applications with .NET. The UI for the app is created using C# instead of JavaScript and the logic behind the app can run server-side or client side. When running server-side, the UI is updated by means of an 'underwater' SignalR connection that updates only the parts of the page that need to be updated. When running client-side, Blazor is using a technique called WebAssembly which allows you to run .NET code inside your browser. To learn more about Blazor start reading at 'Introduction to ASP.NET Core Blazor' in the Microsoft documentation. There are also numerous video's to be found on YouTube. My suggestion to start with is this one: Blazor, a new framework for browser-based .NET apps by Steve Sanderson, the inventor of Blazor.
What is Fluent Design?
Fluent is Microsoft's open-source, cross-platform design system that gives you the frameworks you need to create engaging product experiences—accessibility, internationalization, and performance included. It is not limited to web applications but can also be used for desktop- and mobile apps and is used as the design guidline for products as Office 365 and Windows itself. In this case we will be looking at the web framework part, called Fluent UI (source), which gives you styles and controls that can be used in your application. Fluent UI web represents a collection of utilities and components for building web applications.
Now what?
So now that we now what a Blazor application is and that we want to style it with Fluent UI, we can start to put the pieces together. For this blog I am using the Blazor server-side template but the step for getting it working with a Blazor client-side (or so called Wasm) app are almost the same. As said at the start of this post, your app will be styled with Bootstrap out of the box. It looks ok-ish but I wouldn't recommend this rather basic look for a production grade application. Fortunately there are a lot of component libraries available making it easy to bring more Bootstrap design elements and style into your application. Likewise you can also find libraries to bring a Material Design look to your app. In this case we want to look at Fluent UI as the design system.
Fluent UI Web
If you look at the readme in the Fluent UI Web repo, you'll see that there are actualy three seperate but related parts to the family. Two of them are built with and to be used with React, the other is built as a Web Components implementation (so not dependant on a specific underlying JavaScript framework but still using JavaScript in it's base). Blazor supports JavaScript interop. That means Blazor components are capable of using any library or API that JavaScript is able to use. C# code can call into JavaScript code, and JavaScript code can call into C# code. So theoretically we could use a React implementation of Fluent UI in Blazor. You would however need to write a lot of plumbing code get them communicating and working well together. Besides that, I think it would be a bit silly to use a .NET framework capable of doing UI without Javascript, to call into a Javascript framework to do UI. This means that basically the only viable option out of these 3 would be to use the Web Components part.
There is also an open source Blazor component library for Fluent UI, called BlazorFluentUI, in development. The code is hosted on GitHub where it is described as a "Simple port of Fluent UI React components and style to Blazor". It seems well maintained and comes with regular updates. As can be seen in the readme, it is not a complete port yet. A number of controls that are available in the React version still have a ToDo status. In one of my next posts I'll take a closer look at this library.
To sum it up, we have 3 options of adding Fluent UI Web to a Blazor application:
- Use the available React components - impractical and time consuming (because of needed plumbing)
- Use the Web components - this is what we will focus on in this post
- Use the BlazorFluentUI packages - will revisit in another post
We have the building blocks. Where is the assembly manual?
Let's get started with creating a new Blazor Server application. In my case I'm working from Visual Studio but using a CLI or Visual Studio Code work equally well of course.
In the next screen we'll give the project a name (FluentUIWebBlazor)) and a location to put the files in. Then we select the target framework to run on. I'm going with .NET 5.0 here but 3.1 is also an option. I won't be using authentication or Docker support this time.
Click on create and shortly after the project is ready and the application can be run. If you start the app without debugging (CTRL+F5), changes to the source will automatically trigger a recompilation, giving you a sort of hot reload capability. You should be looking at something like this:
The first thing we need to do now is to de-Bootstrap this:
- In the wwwroot folder, delete the bootstrap folder
- In Pages\_Host.cshtml remove the link to the bootstrap style sheet (the line with <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />)
- In MainLayout.razor.css remove the .sidebar block (lines 11-14) and remove the line with the background color from the .top-row block
- In NavMenu.razor.css also remove the line with the backgound color from the .top-row block
If you run the app again it should now look something like this:
Not a real improvement yet, right? But we'll work on that next.
- Start by adding the fluentui/web-components script from the web-components repository. As described in the readme, we can add the script either by installing it locally (with npm or yarn) or by referencing an online version from https://unpkg.com/. This last option might seem easier, but you will run into CORS errors with the server side template if you do not take the necessary measures. As that goes beyond the scope of this post, we will use the npm version. Install the package by issuing the following command in the root folder of the solution:
npm install --save @fluentui/web-components
This will create a node_modules folder with the @fluentui folder and, as you kind of expect with node, a load of other required folder and packages. - Create a
script
folder inside yourwwwroot
folder and copy thenode_modules\@fluentui\web-components\dist\web-components.min.js
there (or the file without min in the name to have the uncompressed script) - Add the following line right before the closing body tag in _Host.cshtml:
<script type="module" src="script/web-components.js"></script>
- Encapsulate the rest of the body section in the same file with a <fluent-design-system-provider use-defaults> tag so that it looks like this when completed:
<fluent-design-system-provider use-defaults>
<component type="typeof(App)" render-mode="ServerPrerendered" />
<div id="blazor-error-ui">
<environment include="Staging,Production">
An error has occurred. This application may no longer respond until reloaded.
</environment>
<environment include="Development">
An unhandled exception has occurred. See browser dev tools for details.
</environment>
<a href="" class="reload">Reload</a>
<a class="dismiss">??</a>
</div>
</fluent-design-system-provider>
<script src="_framework/blazor.server.js"></script>
<script type="module" src="script/web-components.js"></script> - Add some Fluent UI web components to the index.razor file:
<fluent-card> <h2>Hello World!</h2> <fluent-button appearance="accent">Click Me</fluent-button> </fluent-card>
Running it with these changes should give you something like below. It doesn't look quite right yet, but we can see that the script is loaded and the Fluent UI components, like the card and button are working. - And also add them to the menu by replacing the <div> with the menu in NavMenu.razor with:
<fluent-menu> <fluent-menu-item> <a class="nav-link" href=""> <span class="oi oi-home" aria-hidden="true"></span> Home </a> </fluent-menu-item> <fluent-menu-item> <a class="nav-link" href="counter"> <span class="oi oi-plus" aria-hidden="true"></span> Counter </a> </fluent-menu-item> <fluent-menu-item> <a class="nav-link" href="fetchdata"> <span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data </a> </fluent-menu-item> </fluent-menu>
- Replace the contents of the Counter.razor file with:
@page "/counter" <h1>Counter</h1> <p> Current count: <fluent-badge appearance="@badgeAppearance">@currentCount</fluent-badge> </p> <fluent-button appearance="accent" @onclick="IncrementCount">Click me</fluent-button> @code { private int currentCount = 0; private string badgeAppearance = "neutral"; private void IncrementCount() { currentCount++; badgeAppearance = currentCount % 2 == 0 ? "neutral" : "accent"; } }
- And finally add a bit of styling to the site.css file:
fluent-card { padding: 16px; display: flex; flex-direction: column; } h2 { font-size: var(--type-ramp-plus-5-font-size); line-height: var(--type-ramp-plus-5-line-height); } fluent-card > fluent-button { align-self: flex-end; } fluent-menu { margin-top: 20px; }
Et voilà:
You can see in the code from step 7 that the web components can use normal Blazor event handling (here for dealing with the click of the button). Also the appearance of of the badge component that shows the number of clicks can be changed from Blazor code.
Conclusion
It is not that hard to restyle your Bootstrap standard styled Blazor application. The result that we have here is not finished completely from a styling perpective. I'll leave that as an exercise to the reader. My main point was to show you you can use Fluent UI in your Blazor app. We leveraged web components to do so. No need to use JavaScript interop to integrate the components into Blazor. It is by the way entirely possible to do that if you want/need to.
Comments
Thank you for your post! I am just trying to define this problem right now. Use Blazor to end up running Javascript components has no much sense for me (@fluentui/web-components). There is a Fluent UI Theme (light) in the premium version o Radzen controls and the BlazorFluentUI (which i am currently giving a try). It is very strange noone else is supporting this. Even Microsoft would have to build a decent Blazor native Fluent UI Library (no javascript). Do you know another library (even a paid one) ?
HoracioComments are closed