Blazor Markdown



Blazor is designed to be a platform where you create a complete web application and we saw that in the last experiment with Blazor where we created a stand-alone search site for my blog. But like any tool in our toolbox, it isn’t always the right one for the job.

Take my blog for example, it’s pretty much a read-only site with the content stored in GitHub as markdown that I use Hugo to convert into HTML files. Now sure, it’s possible to do it as a Blazor WASM application, we could get a .NET Markdown library could be used and the pages generated on-the-fly, but that’d an inefficient way to have my website run and would provide a sub-optimal experience to readers.

But if we want to integrate the search app that we’ve previously built, how do we go about that?

  • I wanted to give this a go to see just how simple it might be to create a text editor where you can type in markdown and get instant live preview (of the resulting HTML), using Blazor. Turns out, with a little help from Markdig, it's really pretty straightforward.
  • Find out how to create a CRUD app using Blazor and ASP.NET Core. This blazor app also shows how to call JavaScript from Blazor code.
  • Can’t show Blazor Spinners (with animation) here as these pages are generated using Jekyll which uses Markdown; not ASP.NET Core. So the animations above are an Animated Gif captured from a running Blazor app. I’ve blogged about this in my next post.

I’m thrilled to announce that Blazor WebAssembly is now officially released. This is a fully-featured and supported release of Blazor WebAssembly that is ready for production use.

Understanding How Blazor Starts

To think about how we can run Blazor WebAssembly within another application we need to learn a bit about how a Blazor WebAssembly application runs.

When you create a new project there’s a file called wwwroot/index.html that you might never have dug into, but this is an important piece of the puzzle. It looks like this:

And really, it’s pretty simple, the important pieces that we need are these two lines:

We’ll get to the <app> element shortly, but first, let’s take a look at the JavaScript file. You might notice that this file doesn’t appear anywhere on disk, and that’s because it’s part of the build output. You can find the source of this on GitHub in the ASP.NET Core repository at src/Components/Web.JS/src/Boot.WebAssembly.ts (at the time of writing anyway). This file shares some stuff in common with Blazor Server, but with the main difference of using the MonoPlatform which does a bunch of WASM interop.

This file is critical, without it your Blazor application won’t ever start up since it’s responsible for initializing the WASM environment that hosts Mono (by injecting a script into the DOM) and then it will use another generated file, _framework/blazor.boot.json, to work out what .NET DLL’s will need to be loaded into the Mono/WASM environment.

Blazor Markdown

So you need to have this JS file included and the _framework folder needs to be at the root since that’s how it finds the JSON file (see this comment).

Editor

Lazy-Loading Blazor

An interesting aside which I came across while digging in the source is that you can delay the load of Blazor by adding autostart='false' to the <script> tag, as per this line and then call window.Blazor.start() in JavaScript to start the Blazor application.

I’m not going to use it for this integration, but it’s good to know that you can have a user-initiated initialisation, rather than on page load.

Placing Your Blazor App

Now that we understand what makes our Blazor app start, how do we know where in the DOM it’ll appear? Well, that’s what the <app> element in our HTML is for, but how does Blazor know about it?

It turns out that that is something that we control from our Startup class:

See how on line 14 we’re using AddComponent and specifying a DOM selector of app? That’s how it knows what element in the DOM the application will start. This is something that you can change, maybe make it a selector to a ID of a DOM element or to a <div>, or to anything else that you want, but it’s not that important, so I just leave it as <app>.

Blazor Markdown

Aside: I haven’t tried it yet, but given that you specify the DOM element and the entry component (via generics, this points to App.razor in the above sample) you could potentially have multiple independent Blazor apps running on a page. Why would you do this, I have no idea… but you can in theory!

Hosting Blazor

When it comes to hosting Blazor WASM there are a few options but I want to focus on the Azure Storage static sites approach, which is how my blog is hosted.

First thing we’ll need to do is publish the app in Release mode using dotnet publish --configuration Release. From that we’ll grab the contents of the bin/Release/{TARGET FRAMEWORK}/publish/{ASSEMBLY NAME}/dist/_framework folder, which will contain blazor.boot.json, blazor.server.js, blazor.webassembly.js, a folder called _bin and a folder called wasm.

We want to copy this _framework folder and place it in the root of our static site, maintaining all the paths so that Blazor can start up.

Note: According to the docs you can change the content-root and path-base when hosting using dotnet run but I haven’t found them working when it’s published. Also, Hugo is very aggressive at setting absolute paths so I found it easiest to put my WASM files in the same structure that dotnet run used.

Since this is a search application let’s create a new page called Search and put in our required HTML:

Now generate your static site (or whatever host you’re using) and navigate to the /search router.

If everything has gone correctly you’ll have just received an error!

Sorry, there’s nothing at this address.

Blazor Routing

If you remember back to our last post we learnt about the @page directive in Razor Components. Here you specify the route that the page will match on and up until now we’ve had @page '/' there. But, we’re now on /search and Blazor’s routing engine has looked at the URL and executed your App.razor component:

Since the Router didn’t find a matched route to use RouteView against it’s fallen through to NotFound and that is why we have this error!

Blazor Markdown

Don’t worry, it’s an easy fix, just update the @page directive to match the route that you want it to match on in your published site or simplify your App.razor to not care about routing.

Once a new publish is done and the files copied across it’ll be happy.

Conclusion

Blazor Render Markdown

Blazor markdown viewer

Blazor is a great way which we can build rich applications, but there is value in generating static content upfront and using Blazor to enhance an application rather than own it.

Blazor Markdown Renderer

Here we’ve taken a bit of a look at the important files used to run a Blazor application within an HTML page and we’ve also looked at what it takes to drop it into some other kind of application.