Plugin.Maui.LoadingHud is a tiny native loading overlay for .NET MAUI. Lottie, image or spinner, configured once and overridable per screen.
Every app needs a loading indicator. It sounds like the simplest part of a project.
Then you build one in .NET MAUI, and the problems start.
The problem
Most MAUI loaders are built on pages, popups or modal navigation. That works for a demo, but in a real app it causes trouble:
- 😵 Showing the loader fires
OnAppearing/OnDisappearingon your pages - 🔒 Calling
Show()inside anotherShow()can leave the loader stuck on screen - 🪟 Two loaders can overlap
- 🔁 You repeat the same colors, text and animation on every screen
I hit all of these in a production app, so I stopped patching and built a loader that doesn't go through MAUI navigation at all.
The solution: Plugin.Maui.LoadingHud ✨
It's a native overlay (an Android Dialog and an iOS UIWindow). Your pages never know it's there, so no lifecycle events fire.
using (LoadingHud.Show("Loading..."))
await api.GetDataAsync();
When the using block ends, the loader closes, even if your code throws.
Set it up once
Add your defaults in MauiProgram.cs. Every option is optional.
builder.UseLoadingHud(o =>
{
o.Text = "Please wait..."; // default text everywhere
o.Lottie = "loader.json"; // Resources/Raw
o.Image = "logo.png"; // Resources/Images
o.IndicatorColor = Colors.White;
o.BackgroundColor = Color.FromArgb("#80000000");
o.OverlayColor = Color.FromArgb("#66000000");
o.TextColor = Colors.White;
o.IndicatorSize = 40;
o.LottieSize = 100;
o.ImageSize = 60;
o.Spacing = 8; // gap between loader and text
});
Set nothing and you get a clean white spinner.
Change it for one screen
using (LoadingHud.Show("Uploading...", lottie: "upload.json")) { ... }
using (LoadingHud.Show(image: "logo.png")) { ... }
using (LoadingHud.Show("")) { ... } // no text this time
The signature is simple:
IDisposable Show(string? text = null, string? lottie = null, string? image = null)
How it decides what to show
| Priority | Loader |
|---|---|
| 1 |
lottie passed to Show()
|
| 2 |
image passed to Show()
|
| 3 |
Lottie from MauiProgram
|
| 4 |
Image from MauiProgram
|
| 5 | Activity indicator (spinner) |
Text works the same way: the text you pass wins, otherwise the default from MauiProgram. If a Lottie or image file can't be found, it quietly falls back to the spinner instead of crashing.
How it works under the hood
-
Native overlay: Android uses a
Dialogand iOS uses aUIWindowabove your app. MAUI navigation is never involved. -
Reference counting: each
Show()adds one, eachDispose()removes one, and the loader closes when the count reaches zero. Nested calls just work. -
Hide()as a safety net: it closes everything at once, and tokens from before it are ignored. - Memory: native views are disposed on hide. On iOS only the last Lottie stays cached, and Android uses Lottie's own cache.
-
Threading: call
Show()/Hide()from any thread. The UI work is marshalled to the main thread.
Quick example
private async void Save_Clicked(object sender, EventArgs e)
{
using (LoadingHud.Show("Saving..."))
{
await SaveAsync();
using (LoadingHud.Show("Syncing...")) // nested: no clash
await SyncAsync();
}
}
Get it
📦 NuGet: https://www.nuget.org/packages/Plugin.Maui.LoadingHud
💻 GitHub: https://github.com/PrathamsGithub/Plugin.Maui.LoadingHud
dotnet add package Plugin.Maui.LoadingHud
Android and iOS are supported. Other targets compile but show nothing.
Feedback welcome
Found a bug on a device I didn't test, or want another option? Open an issue or send a PR on GitHub. If it saves you time, a ⭐ helps others find it. 🙌


Top comments (0)