Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
davidortinau avatar

Maui Hybridwebview

  • 29 installs
  • 163 repo stars
  • Updated July 6, 2026
  • davidortinau/maui-skills

Embeds web content in .NET MAUI apps using HybridWebView with JavaScript-C# interop, bidirectional communication, and raw messaging.

About

Guides embedding web content in .NET MAUI apps via HybridWebView with JavaScript-C# interop, bidirectional communication and raw messaging. A developer uses it when hosting and communicating with web content inside a MAUI app.

  • HybridWebView for embedding web content
  • JavaScript-C# interop and bidirectional messaging

Maui Hybridwebview by the numbers

  • 29 all-time installs (skills.sh)
  • Ranked #664 of 1,039 Mobile Development skills by installs in the Skillselion catalog
  • Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/davidortinau/maui-skills --skill maui-hybridwebview

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs29
repo stars163
Last updatedJuly 6, 2026
Repositorydavidortinau/maui-skills

What it does

Embeds web content in .NET MAUI apps using HybridWebView with JavaScript-C# interop, bidirectional communication, and raw messaging.

Files

SKILL.mdMarkdownGitHub ↗

HybridWebView in .NET MAUI

HybridWebView hosts HTML/JS/CSS content inside a .NET MAUI app with bidirectional C#↔JS communication. It is not a general browser control — it is designed for local web content shipped with the app.

Common gotchas

IssueFix
Blank white screenWeb assets missing from Resources/Raw/wwwroot or DefaultFile not set
JS interop silently failsMissing <script src="_hwv/HybridWebView.js"></script> in HTML
InvokeJavaScriptAsync returns nullReturn type missing [JsonSerializable] attribute in JsonSerializerContext
JS → C# calls do nothingSetInvokeJavaScriptTarget not called before JS invokes C# methods
Serialization crash with trimmingNot using source-generated JsonSerializerContext

⚠️ Bridge script is mandatory

The HTML page must include the bridge script before any app scripts:

<!-- ✅ Correct order -->
<script src="_hwv/HybridWebView.js"></script>
<script src="scripts/app.js"></script>

<!-- ❌ Wrong — app.js loads before bridge, interop calls fail silently -->
<script src="scripts/app.js"></script>
<script src="_hwv/HybridWebView.js"></script>

JSON serialization — every type must be registered

Every parameter type and return type used in InvokeJavaScriptAsync must have a [JsonSerializable] entry:

// ✅ Correct — all interop types registered
[JsonSerializable(typeof(int))]
[JsonSerializable(typeof(string))]
[JsonSerializable(typeof(Person))]
internal partial class MyJsonContext : JsonSerializerContext { }

// ❌ Wrong — adding a new type to interop without registering it
// This causes silent null returns or runtime exceptions
Rule: When you add a new type to the interop surface, you must add a [JsonSerializable(typeof(T))] attribute to the context. Forgetting this is the #1 cause of mysterious interop failures.

SetInvokeJavaScriptTarget — timing matters

// ✅ Set target BEFORE the web page loads and JS calls C#
myHybridWebView.SetInvokeJavaScriptTarget(new MyJsBridge());

// ❌ Setting it after JS already tried to call — calls are lost

⚠️ Call SetInvokeJavaScriptTarget during page construction or OnAppearing, not lazily.

Exception handling (.NET 9+)

JS exceptions thrown during InvokeJavaScriptAsync are forwarded to .NET. Always wrap interop calls:

// ✅ Catches JS errors
try
{
    var result = await myHybridWebView.InvokeJavaScriptAsync<string>(
        "riskyFunction", MyJsonContext.Default.String);
}
catch (Exception ex)
{
    Debug.WriteLine($"JS error: {ex.Message}");
}

// ❌ Unhandled JS exception crashes the interop pipeline
var result = await myHybridWebView.InvokeJavaScriptAsync<string>(
    "riskyFunction", MyJsonContext.Default.String);

Trimming / NativeAOT pitfalls

Trimming is disabled by default in MAUI projects. If you enable it:

  • ⚠️ You must use source-generated JsonSerializerContext (not reflection-based serialization)
  • ⚠️ Set JsonSerializerIsReflectionEnabledByDefault to false
  • Using JsonSerializerContext as shown above is recommended regardless of trimming settings
<PropertyGroup>
  <PublishTrimmed>true</PublishTrimmed>
  <JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault>
</PropertyGroup>

Decision framework — typed interop vs raw messages

NeedUse
Structured data exchange with type safetyInvokeJavaScriptAsync / InvokeDotNet with JsonSerializerContext
Simple string payloads, fire-and-forgetSendRawMessage / RawMessageReceived
Calling C# from JS with return valuesInvokeDotNet (target must be set first)
Multiple JS functions to callTyped interop — one InvokeJavaScriptAsync per function

Quick checklist

  • [ ] Web content is under Resources/Raw/wwwroot
  • [ ] index.html includes <script src="_hwv/HybridWebView.js"></script> before app scripts
  • [ ] DefaultFile is set (or defaults to index.html)
  • [ ] Every interop type has a [JsonSerializable] entry in a JsonSerializerContext
  • [ ] SetInvokeJavaScriptTarget is called before JS invokes C# methods
  • [ ] InvokeJavaScriptAsync calls are wrapped in try/catch (.NET 9+)
  • [ ] If trimming enabled: source-generated JSON serialization configured

Related skills

Mobile Developmentfrontendintegrations

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.