Docs Bridge
Alpha documentation — APIs may change between 0.x releases.

Call C# from JavaScript

A native contract is a C# class whose annotated methods become generated, typed TypeScript methods.

Define the native contract

Add GreetingModule.cs to the host project:

using Vidra.Bridge;

namespace MyApp;

public record GreetArgs(string Name);
public record GreetResult(string Message);

[BridgeModule("greeting")]
public sealed class GreetingModule : BridgeModuleBase
{
    [BridgeMethod("greet")]
    public Task<GreetResult> GreetAsync(
        GreetArgs args,
        CancellationToken ct)
    {
        return Task.FromResult(
            new GreetResult($"Hello, {args.Name} from C#!"));
    }
}

The contract name and method name become the generated JavaScript API. Argument and result records become TypeScript interfaces.

Register the module

Pass the module to UseVidra in MauiProgram.cs:

builder
    .UseMauiApp<App>()
    .UseVidra(dispatcher =>
    {
        dispatcher.Register(new GreetingModule());
    });

Registration makes the implementation available to the host. It does not grant the WebView permission to call it.

Generate the TypeScript API

Build or run the app:

npm run dev

The host project already sets VidraTsOutputDir to ui/src/generated. The build writes a generated greeting proxy and exports it from ui/src/generated/index.ts.

Generated files begin with Auto-generated by vidra-codegen. Do not edit. Keep them committed, but change the C# declaration rather than editing TypeScript output.

Grant WebView access

After the first build generates the application access catalog, add the method token to vidra.config.ts:

import { defineConfig } from "@vidra-dev/sdk/config";
import { native } from "./ui/src/generated/access.js";

export default defineConfig({
  bridge: {
    allow: [native.greeting.greet],
  },
});

vidra dev rebuilds and relaunches the host when this policy changes. Calls to registered but ungranted methods fail with NATIVE_ACCESS_DENIED.

Call C# from the UI

import { greeting } from "./generated/index.js";

const { message } = await greeting.greet({ name: "Ada" });
console.log(message);

The call is typed end to end:

  • Omitting name is a TypeScript error.
  • The result is inferred as { message: string }.
  • Renaming the C# method or DTO changes generated output.
  • A stale web/native contract fingerprint is rejected when the WebView starts.

Built-in native contracts

Vidra’s built-in APIs use the same contract model. They are generated into @vidra-dev/sdk and do not need registration, but they still need an entry in vidra.config.ts:

import { clipboard } from "@vidra-dev/sdk";

const { text } = await clipboard.getText();

See Built-in native capabilities for the complete reference, or continue with Call JavaScript from C#.

See Control bridge access for events, filesystem scopes, and update compatibility.