Hello everyone, I'm Wolf at the End of the Desert.
The Dotnet9 website has been rebuilt with Blazor, and the access speed is really fast. At the same time, leveraging Blazor's interactive capabilities, the site owner has also added several online tools. This article shares the process of rebuilding with Blazor, hoping to provide a reference for everyone when making technical choices for website development.

1. First, a Word on Razor Pages
The previous version of the website frontend used Razor Pages. At the time, this technology stack was chosen mainly for SEO optimization.
Regarding which is better between MVC and Razor Pages, we'll only discuss the relative advantages of Razor Pages here.
First, Razor Pages is simpler and more intuitive compared to MVC. Because Razor Pages encapsulates both the view and processing logic in the same page, developers can more easily understand and maintain the code. For small projects or applications with only a few pages, Razor Pages can provide faster development speed and cleaner code structure—this was the main reason the site owner chose to refactor from MVC to Razor Pages.
Second, Razor Pages has certain advantages in SEO (Search Engine Optimization). Since Razor Pages encapsulate view and processing logic in the same page, search engines can more easily understand and index the page content. This is an important consideration for applications that need better search engine rankings.
So, if Razor Pages has advantages, why switch to Blazor now? Because Blazor might be an even better choice—let's continue.
2. The Key Point: Let's Talk About Blazor
Blazor is an emerging web development framework that allows developers to write web applications using the C# language without necessarily using JavaScript. Well, we can say "minimizing" the use of JavaScript; completely avoiding it is not realistic. Compared to Razor Pages and MVC, Blazor offers a completely new development model with many unique advantages and suitable scenarios.
First, Blazor provides a true frontend development experience. In traditional web development, frontend developers use JavaScript to handle page interactions and dynamic effects, while backend developers handle business logic and data operations. This separation can lead to communication and collaboration issues between developers. Blazor uses C# to write frontend code, allowing frontend and backend developers to use the same language and tools, enabling more efficient collaborative development.
Second, Blazor offers better performance and user experience. Blazor provides two modes: client-side and server-side (we'll discuss Blazor hybrid mode another time):
- Client-side mode: Blazor uses WebAssembly technology to run compiled binary code directly in the browser, achieving performance close to native applications.
- Server-side mode: Compared to traditional page refreshes based on HTTP requests, Blazor uses SignalR connections for real-time data updates and two-way binding, providing a faster and smoother user experience.
Additionally, Blazor has better reusability and component-based development. Blazor provides a rich set of component libraries and tools that help developers quickly build beautiful and powerful interfaces. Developers can encapsulate commonly used UI components into reusable components, improving development efficiency and code quality.
Moreover, Blazor supports modern frontend development technologies and tools. Developers can integrate Blazor with existing JavaScript libraries and frameworks, such as React, Vue.js, etc.
In summary, Blazor is a better choice compared to Razor Pages and MVC, especially for projects that require a better frontend development experience, better performance and user experience, and better reusability and component-based development. However, choosing which development model to use still depends on the specific needs of the project and the preferences of the development team. Regardless of the model chosen, it's important to make a reasonable decision based on the project's actual situation and follow good design principles and best practices during development.
3. Let's Talk Again: Why Use Blazor Again?
Last year, the site owner developed a version of the website frontend using Blazor Server. At that time, due to the problematic reconnection experience, the site owner chose to refactor with Razor Pages.
The turning point for the site owner to return to Blazor was on June 13th - the release of .NET 8 Preview 5. The VS2022 preview also released the Blazor Web App project template, and various tech communities went wild discussing it. The site owner tried adding Razor components to Razor Pages. Microsoft is truly impressive—they aim to make Blazor components meet all web UI needs on both client and server sides.
But currently, Razor components in this mode cannot interact, and the page also shows a reconnection grayed-out UI. So the site owner simply refactored using Blazor Server directly. After several days of hard work, the website frontend has been completely replaced with Blazor Server from Razor Pages. The annoying reconnection issue is also resolved. Now the website is very fast to access—maybe it's an illusion, but personally, it feels great. (Regarding the reconnection issue, refer to Microsoft documentation ASP.NET Core Blazor SignalR guidance and the article by Token How to cancel the annoying reconnection of Blazor Server?.)
Both Razor Pages (MVC) and Blazor use Razor syntax, so theoretically, switching is seamless with minimal changes to core code. The project code file structure comparison is shown in the screenshot below. I won't go into detail—check the source code if interested; both versions of the code are available.
| Razor Pages version project structure | Blazor Server version project structure |
![]() |
![]() |
4. Interaction Convenience of Blazor: Bringing a Few Online Tools
For page event handling, using Blazor is convenient. Below are four small tools added last night:

Interested friends can click to try: https://dotnet9.com/tools. We'll directly paste the code for the four tools—you might fall in love with Blazor's coding style.
4.1. JSON Formatter
Access URL: https://dotnet9.com/tools/jsonformatter

Page code:
@page "/tools/jsonformatter"
@using System.Text.Json
<PageTitle>@_title</PageTitle>
<MApp>
<h2 style="margin-bottom: 30px; margin-top: 10px; text-align: center;">@_title</h2>
<div>
<MTextarea BackgroundColor="grey lighten-2" Solo
Color="orange orange-darken-4" TValue="string" @bind-Value="_inputJson"
Label="Enter JSON" Rows="8" style="font-size:12px;" RowHeight="15" AutoGrow/>
</div>
<div>
<MButton Color="success" class="ma-2" OnClick="() => FormatJson(true)">Format</MButton>
<MButton Color="lime" OnClick="() => FormatJson(false)">Compress</MButton>
</div>
<div>
<MTextarea BackgroundColor="amber lighten-4" Solo
Color="orange orange-darken-4" TValue="string" @bind-Value="_formattedJson"
Label="Formatted or Compressed JSON" Rows="8" style="font-size:12px;" RowHeight="15" AutoGrow/>
</div>
</MApp>
@code
{
private const string? _title = "Tools - JSON Formatter";
private string? _inputJson;
private string? _formattedJson;
private void FormatJson(bool writeIndented)
{
try
{
var jsonObject = JsonDocument.Parse(_inputJson).RootElement;
_formattedJson = JsonSerializer.Serialize(jsonObject, new JsonSerializerOptions { WriteIndented = writeIndented });
}
catch (JsonException)
{
_formattedJson = "Invalid JSON format";
}
}
}
4.2. Online String Encoding Tool
Access URL: https://dotnet9.com/tools/string-encoder

Page code:
@page "/tools/string-encoder"
<PageTitle>@Title</PageTitle>
<MApp>
<h2 style="margin-bottom: 30px; margin-top: 10px; text-align: center;">@Title</h2>
<p>
<MTextarea BackgroundColor="grey lighten-2"
Color="cyan" Solo TValue="string" @bind-Value="_inputString"
Label="Enter string"/>
</p>
<p>
<MTextarea BackgroundColor="amber lighten-4" Solo
Color="orange orange-darken-4" TValue="string" @bind-Value="_encodedOrDecodeString"
Label="Encode/Decode result"/>
</p>
<p>
<MButton OnClick="@Encode">Encode</MButton>
<MButton OnClick="@Decode">Decode</MButton>
<MButton OnClick="@Clear">Clear</MButton>
</p>
</MApp>
@code {
private const string Title = "Tools - Online String Encoding Tool";
private string? _inputString;
private string? _encodedOrDecodeString;
private void Encode()
{
_encodedOrDecodeString = System.Web.HttpUtility.UrlEncode(_inputString);
}
private void Decode()
{
_encodedOrDecodeString = System.Web.HttpUtility.UrlDecode(_inputString);
}
private void Clear()
{
_inputString = string.Empty;
_encodedOrDecodeString = string.Empty;
}
}
4.3. Countdown
Access URL: https://dotnet9.com/tools/countdown

Page code:
@page "/tools/countdown"
<PageTitle>@Title</PageTitle>
<MApp>
<h2 style="margin-bottom: 30px; margin-top: 10px; text-align: center;">@Title</h2>
<p>
<MTextField Label="Duration (seconds)" Type="number" TValue="int" @bind-Value="@_duration"/>
</p>
<p>
<MButton Color="success" class="ma-2" OnClick="@StartCountdown" Disabled="@_isCountingDown">Start</MButton>
<MButton Color="pink" class="ma-2 white--text" OnClick="@PauseCountdown" Disabled="!_isCountingDown">Pause</MButton>
<MButton Color="deep-orange" class="ma-2 white--text" OnClick="@ResetCountdown" Disabled="!_isCountingDown">Reset</MButton>
Remaining time (seconds): @_remainingTime
</p>
<div class="text-center">
<MProgressCircular Value="@_remainingTimePercent" Size="100" Width="15" Rotate="360" Color="teal">@_remainingTime</MProgressCircular>
</div>
</MApp>
@code {
private const string Title = "Tools - Countdown";
private int _duration = 20;
private int _remainingTime;
private int _remainingTimePercent;
private bool _isCountingDown;
private bool _isPause;
private CancellationTokenSource? _countdownTokenSource;
private async Task StartCountdown()
{
if (_duration < 0)
{
_duration = 10;
}
if (_isCountingDown)
{
return;
}
_isCountingDown = true;
if (!_isPause || _remainingTime <= 0)
{
_remainingTime = _duration;
ChangeRemainingTimePercent();
}
_countdownTokenSource = new CancellationTokenSource();
await Countdown(_countdownTokenSource.Token);
}
private void PauseCountdown()
{
if (!_isCountingDown)
{
return;
}
_isCountingDown = false;
_isPause = true;
_countdownTokenSource?.Cancel();
}
private async void ResetCountdown()
{
_isPause = false;
if (_isCountingDown && _countdownTokenSource != null)
{
await _countdownTokenSource.CancelAsync();
}
_remainingTime = _duration;
_isCountingDown = false;
ChangeRemainingTimePercent();
}
private async Task Countdown(CancellationToken cancellationToken)
{
while (_remainingTime > 0)
{
await Task.Delay(1000, cancellationToken);
_remainingTime--;
ChangeRemainingTimePercent();
if (cancellationToken.IsCancellationRequested)
{
return;
}
}
_isCountingDown = false;
}
private async void ChangeRemainingTimePercent()
{
_remainingTimePercent = (int)(_remainingTime * 100.0 / _duration);
await InvokeAsync(StateHasChanged);
}
}
4.4. Timestamp Conversion
Access URL: https://dotnet9.com/tools/timestamp
The site owner wrote an article about this before—check it out here: Use Blazor to make a simple online timestamp conversion tool.
5. Summary
There may be bugs on the website—no, there definitely are bugs—and the site owner will continue to refactor and iterate.
I am very glad to share this joy of refactoring the website frontend with everyone. Wishing everyone a happy Dragon Boat Festival.
- Website address: https://dotnet9.com/
- Source code: https://github.com/dotnet9/Dotnet9
- .NET version: .NET 8.0.0-preview.5.23280.8

