Hey everyone!
I got a few messages asking me how I created my free, open source habit tracker app OpenHabitTracker so I decided to write a post about it.
You can see the source code on GitHub.
Why Blazor
I wanted to create an app that would work on as many platforms as possible and share as much code as possible between all platforms.
I compared all cross platform C# frameworks that were available:
Framework | First Release | UI Technology | Windows | macOS | Linux | Android | iOS | Web (WASM) |
---|---|---|---|---|---|---|---|---|
.NET MAUI | May 2022 | XAML | Yes | Yes | No | Yes | Yes | No |
Blazor | Sep 2019 | HTML + CSS | Yes | Yes | Yes | Yes | Yes | Yes |
Avalonia | Feb 2015 | XAML | Yes | Yes | Yes | Yes | Yes | Experimental |
Uno Platform | Sep 2018 | XAML | Yes | Yes | Yes | Yes | Yes | Yes |
Xamarin.Forms | May 2014 | XAML | Yes | No | No | Yes | Yes | No |
Xamarin was replaced by MAUI and MAUI doesn't support Web - that left Avalonia (with poor web support at that time), Uno and Blazor.
I have used a few open sourced project managed by volunteers and it did not have the best experience, so I wanted to choose a project backed by a company.
I was working with WPF for 5 years and in that time I found out that I really don't like XAML, so I was really happy when Blazor came out.
My first Blazor project was only with WASM and Windows through WebView2 in WinForms / WPF. When Maui Blazor Hybrid was announced I knew that Blazor was a good choice.
How I started
I started by creating a project from every Visual Studio Blazor project template in a single solution and then I started to move all the files that were the same into a shared library.
At first, I had only a single shared library, but later I split it into OpenHabitTracker
of type <Project Sdk="Microsoft.NET.Sdk">
that holds the core classes and to OpenHabitTracker.Blazor
of type <Project Sdk="Microsoft.NET.Sdk.Razor">
that holds the razor files. This way the logic is separated from the UI and the razor files contain few lines of C# code which is a good thing because Visual Studio editor for razor files can behave strangely at times, especially with @code
blocks. The editor behaves better if you have your C# code in .razor.cs
code behind files, but you can just go one step further and have the C# code in a separate library.
At first, I had only my razor components and razor pages in the shared library, I didn't move App.razor
, _Imports.razor
, MainLayout.razor
, JsInterop.cs
, jsInterop.js
, app.css
into the shared library, but later I figured out that you can move all files except Program.cs
and index.html
into the shared library - if there is any platform specific behavior, you can solve it with C# interfaces - even if you need platform specific razor UI, you can still solve it with interfaces and methods that return a RenderFragment
. All .css
and .js
files can be in the shared library and then included to platform specific projects in index.html
with _content/OpenHabitTracker.Blazor/...
for example <link rel="stylesheet" href="_content/OpenHabitTracker.Blazor/app.css" />
.
How I chose the frameworks
I started comparing all the frameworks that support Blazor on desktop and mobile:
Windows only:
- WPF: works great with
Microsoft.AspNetCore.Components.WebView.Wpf
NuGet - WinForms: works great with
Microsoft.AspNetCore.Components.WebView.WindowsForms
NuGet
Windows, Linux, macOS:
- Photino Blazor: works great with
Photino.Blazor
NuGet - Electron.NET: works with
ElectronNET.API
NuGet - long compile times, very large builds, slow startup, opens the GUI window AND a terminal - Chromely: works with
Chromely
NuGet - long compile times, slow startup, opens the GUI window AND a terminal - GitHub repo was archived by the owner on Jan 16, 2023
Windows, macOS, iOS, Android:
- MAUI: works with several Maui NuGet packages and
dotnet workload
s:android
,ios
,maccatalyst
,maui-windows
, see https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-workload
To support WASM, Windows, Linux, macOS, iOS, Android you need at least:
- Blazor WASM
- Maui
- Photino Blazor
I develop on Windows with Visual Studio 2022 and my startup project is usually OpenHabitTracker.Blazor.Photino
because it compiles and starts much faster than OpenHabitTracker.Blazor.Wasm
or OpenHabitTracker.Blazor.Maui
.
Saving user data
I took a look at the different options for storing user data:
Technology | Size Limit | Data Lifetime | Storage Format | Security |
---|---|---|---|---|
Cookies | ~4 KB | Configurable | String | HttpOnly, Secure |
Session Storage | 5-10 MB | Session-based | String | Isolated to tab |
Local Storage | 5-10 MB | Persistent | String | Shared across tabs |
WebSQL | 5 MB+ | Persistent | Relational DB | Deprecated |
IndexedDB | Hundreds of MB | Persistent | Key-Value (Objects) | Same-origin |
Cache Storage (SW) | Large | Configurable | HTTP responses | Origin-bound |
File System Access API | Device-dependent | Persistent | Files/Blobs | Requires Permission |
Because I wanted a large, persistent storage that does not require prompting the user for permission and is not deprecated I chose IndexedDB:
-
https://github.com/wtulloch/Blazor.IndexedDB -
TG.Blazor.IndexedDB
NuGet - awkward, not user friendly, abandoned project -
https://github.com/Reshiru/Blazor.IndexedDB.Framework -
Reshiru.Blazor.IndexedDB.Framework
NuGet - wrapsTG.Blazor.IndexedDB
NuGet, making it much nicer to work with - but it has a huge flaw: it always loads all data - you don't have the ability to load only some objects - repository has been archived by the owner on Jan 26, 2021 -
https://github.com/amuste/DnetIndexedDb -
DnetIndexedDb
NuGet - works well enough
I made two mistakes:
- I chose
Reshiru.Blazor.IndexedDB.Framework
and I even fixed a few things and created https://github.com/Jinjinov/IndexedDB.Blazor (don't use it, it has the same problem) - I used IndexedDB on desktop and mobile - it works, but I think SQLite is a better option.
I switched to DnetIndexedDb
for the browser and to EF Core + SQLite for desktop and mobile.
The user interface
At first, I wanted to write all CSS on my own, but I soon realized that using Bootstrap is not that bad and saves you a lot of time.
That proved to be a good decision when I decided to implement themes and discovered that Bootswatch offers 26 themes for Bootstrap.
I decided to use a free UI library, so I took a look at what was available:
Library | Features | First Release |
---|---|---|
Blazorise | Bootstrap, Bulma, AntDesign, Material | June 2019 |
MudBlazor | Material Design components | April 2020 |
AntDesign Blazor | Inspired by Ant Design | March 2020 |
MatBlazor | Material Design components | February 2019 |
BlazorStrap | Based on Bootstrap 4/5 | April 2019 |
Blazor Bootstrap | Bootstrap 5 components | June 2021 |
In my previous Blazor project, I used the Blazorise UI library because it had the most controls at the time and because it abstracts the CSS to C# enums and classes - that way it was really easy to switch from Bootstrap 4 to Bootstrap 5. The owner of the project is really responsive to ideas and requests (if you are reasonable and show some initiative). Later I found out that I don't actually need any complex components in my project, so now I use only Bootstrap 5.
In my previous Blazor project, I used Font Awesome icons and Google Fonts, but now I use embedded font files and Bootstrap Icons because I think they work better with Bootstrap 5.
In my previous Blazor project, I used CDN to get the CSS, JS and fonts from the web, but I found out that this makes the app noticeably slower. Now I include all CSS, JS and fonts into the project and the app is much faster. The app is around 2 MB larger because of all the included resources, but at 20 MB for the whole app that is only 10% more - for a much faster UI.
Feel free to ask any questions! :)
Top comments (0)