All skills
davidortinau avatar

/maui-hybridwebview

@7796ee4

Guidance for embedding web content in .NET MAUI apps using HybridWebView, including JavaScript–C# interop, bidirectional communication, raw messaging, and trimming/NativeAOT considerations. USE FOR: "HybridWebView", "JavaScript interop", "embed web content", "JS to C# interop", "C# to JavaScript", "web view interop", "raw message", "InvokeJavaScriptAsync", "web content MAUI". DO NOT USE FOR: deep linking from external URLs (use maui-deep-linking), REST API calls (use maui-rest-api), or Blazor Hybrid apps (different from HybridWebView).

Use this Skill: https://skilld.dev/gh/davidortinau/maui-skills/maui-hybridwebview

This session only. Nothing lands on disk.

referenceshybridwebview-api.md

≈1.1k tokens on demand. Your agent reads this file only when SKILL.md points to it.

HybridWebView API Reference

Project Layout

Place web assets under Resources/Raw/wwwroot (the default root). Set a different root with the HybridRootComponent property if needed.

Resources/Raw/wwwroot/
  index.html        ← entry point (default)
  scripts/app.js
  styles/app.css

XAML Setup

<HybridWebView
    x:Name="myHybridWebView"
    DefaultFile="index.html"
    RawMessageReceived="OnRawMessageReceived"
    HorizontalOptions="Fill"
    VerticalOptions="Fill" />

DefaultFile sets the HTML page loaded on start (defaults to index.html).

index.html Structure

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

<!DOCTYPE html>
<html lang="en">
<head><meta charset="utf-8" /></head>
<body>
  <!-- app markup -->
  <script src="_hwv/HybridWebView.js"></script>
  <script src="scripts/app.js"></script>
</body>
</html>

C# → JavaScript (InvokeJavaScriptAsync)

Call a JS function from C# and receive a typed result:

// JS: function addNumbers(a, b) { return a + b; }
var result = await myHybridWebView.InvokeJavaScriptAsync<int>(
    "addNumbers",
    MyJsonContext.Default.Int32,       // return type info
    [2, 3],                            // parameters
    [MyJsonContext.Default.Int32,      // param 1 type info
     MyJsonContext.Default.Int32]);    // param 2 type info

For complex types:

var person = await myHybridWebView.InvokeJavaScriptAsync<Person>(
    "getPerson",
    MyJsonContext.Default.Person,
    [id],
    [MyJsonContext.Default.Int32]);

JavaScript → C# (InvokeDotNet)

From JS, call a C# method exposed on the invoke target:

const result = await window.HybridWebView.InvokeDotNet('MethodName', [param1, param2]);
window.HybridWebView.InvokeDotNet('LogEvent', ['click', 'button1']); // fire-and-forget

Setting the Invoke Target

Register the object whose public methods JS can call:

myHybridWebView.SetInvokeJavaScriptTarget(new MyJsBridge());

public class MyJsBridge
{
    public string Greet(string name) => $"Hello, {name}!";
    public Person GetPerson(int id) => new Person { Id = id, Name = "Ada" };
}

Method parameters and return values are serialized as JSON.

Raw Messages

For unstructured string communication use raw messages instead of typed interop.

C# → JS:

myHybridWebView.SendRawMessage("payload string");

JS → C#:

window.HybridWebView.SendRawMessage('payload string');

Receiving in C#:

void OnRawMessageReceived(object sender, HybridWebViewRawMessageReceivedEventArgs e)
{
    var message = e.Message;
}

Receiving in JS:

window.addEventListener('HybridWebViewMessageReceived', e => {
    const message = e.detail.message;
});

JSON Serialization Setup

Use source-generated JSON serialization. Define a partial context covering every type exchanged between JS and C#:

[JsonSourceGenerationOptions(
    WriteIndented = false,
    PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
[JsonSerializable(typeof(int))]
[JsonSerializable(typeof(string))]
[JsonSerializable(typeof(Person))]
internal partial class MyJsonContext : JsonSerializerContext { }

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
}

JS Exception Forwarding (.NET 9+)

JavaScript exceptions thrown during InvokeJavaScriptAsync are automatically forwarded to .NET as managed exceptions. Wrap calls in try/catch:

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

Trimming and NativeAOT

Trimming and NativeAOT are disabled by default in MAUI projects. If you enable them, ensure JSON source generators are used:

<PropertyGroup>
  <PublishTrimmed>true</PublishTrimmed>
  <JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault>
</PropertyGroup>

Using JsonSerializerContext (source generation) as shown above is the recommended pattern regardless of trimming settings.

Source: SKILL.md on GitHub

No alerts15d4 checks · Risk SAFE
  • Gen Agent Trust Hub15d

    The skill provides documentation and guidance for using the HybridWebView control in .NET MAUI. It describes best practices for JavaScript–C# interop and JSON serialization. No malicious patterns were detected.

  • Socket15d

    No alerts

  • Snyk15d

    Risk: LOW · No issues

  • Runlayer7mo

    1 file scanned · No issues

Signed by skilld at 7796ee4. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 months ago.

Steadyupdated 6 months ago

README badge

README badge for davidortinau/maui-skills/maui-hybridwebview