Build cross-platform desktop apps with React, Vue, or Svelte on a C#/.NET native layer.
The type-safe, lightweight alternative to Electron (Node), Tauri (Rust), and XAML (MAUI · Avalonia · WPF).
Vidra lets .NET teams build cross-platform desktop applications with any modern web framework on a native C#/.NET core. The UI is pure web ( React, Vue, Svelte, Solid, your call ) the native layer is C#, and a type-safe bridge generated from your C# code keeps the two sides permanently in sync. Unlike Electron you never touch Node; unlike Tauri you never touch Rust; unlike MAUI, Avalonia, or WPF you never touch XAML.
Alpha. APIs and templates may change between 0.x releases.
- Stay in C#/.NET. Your native layer, domain logic, and NuGet libraries are all C#. No second backend language for the team to learn.
- Bring any web framework. The UI is a standard web app rendered in a native WebView: React, Vue, Svelte, Solid, or plain HTML. No XAML, no Razor lock-in.
- End-to-end typed contracts. Native methods, event contracts, and JS contracts are declared in C#; generated C#/TypeScript surfaces and startup fingerprints prevent silent drift. Dynamic traffic remains available only through an explicit unsafe escape hatch.
- Lightweight. Uses the OS-native WebView (WKWebView / WebView2) instead of bundling Chromium, keeping apps small and memory-light.
- One bridge, both ways. Native calls, event contracts, and JS contracts all flow over one versioned JSON bridge.
The fastest way to start is the create-vidra-app scaffolder:
npm create vidra-app@latestYou'll be prompted for a project name and an app ID (reverse-domain). Then launch it:
cd my-app
npm run devnpm run dev (which runs vidra dev) starts Vite and the native host for your current
OS together, with Vite HMR and a C# loop: supported edits hot reload into the running app.
On macOS the Mac Catalyst hot-reload agent sometimes drops out mid-session
(dotnet/sdk#55488); when it does, vidra dev
says so and keeps the loop working by rebuilding and relaunching on save.
- .NET 10 SDK
- The .NET MAUI workload:
dotnet workload install maui - Node.js 18+
- macOS targets require Xcode; Windows targets must be built on Windows
Not sure you're set up? Run npm run doctor to check your .NET SDK, MAUI workload, and
(on macOS) Xcode, and print the exact command to fix anything missing.
The vidra CLI is a local dev dependency of each scaffolded app — there's no global
vidra to install. Run it from inside your project via the npm scripts or npx:
npm run dev # start Vite + native host (UI + C# reload on save)
npm run build # build + package for distribution
npm run doctor # verify your environment
npx vidra run # launch the native host only
npx vidra dev --target windows # run a specific desktop target
npx vidra build --target macos # build & package a specific targetAdd --plan to any build to preview every step and the artifact name without
running anything.
vidra build produces a macOS .dmg or a self-contained Windows .zip. Both
run locally as-is, but an app that arrives over the internet also has to satisfy
the OS: macOS wants a Developer ID signature plus notarization, Windows wants an
Authenticode signature. Configure them by environment and vidra build does the
rest — with nothing configured it still builds, it just warns and skips.
| Variable | Purpose |
|---|---|
VIDRA_MACOS_CODESIGN_KEY |
Force a specific macOS signing identity (otherwise a Developer ID Application certificate is preferred for builds) |
VIDRA_NOTARY_PROFILE |
notarytool keychain profile — enables notarization + stapling |
VIDRA_APPLE_ID / VIDRA_TEAM_ID / VIDRA_APP_PASSWORD |
Notarization credentials, as an alternative to a stored profile |
VIDRA_WINDOWS_CERT_PATH / VIDRA_WINDOWS_CERT_PASSWORD |
Authenticode certificate file |
VIDRA_WINDOWS_CERT_THUMBPRINT |
Authenticode certificate already in the Windows store |
VIDRA_WINDOWS_TIMESTAMP_URL |
Timestamp server (defaults to DigiCert) |
npm run doctor reports which of these are in place. The macOS build signs with
the hardened runtime using the Entitlements.plist in your host project — those
entitlements are what keep .NET's JIT alive under it, so keep them.
Windows note: the ZIP bundles the .NET runtime and the WindowsAppSDK, but the WebView2 runtime is a machine-wide install. It ships with Windows 11 and alongside Edge on Windows 10; a machine without it shows a blank window.
A single WebView hosts your web UI; the .NET MAUI host owns all native capability. Calls flow JS → C# as JSON requests over a native message channel (with a custom-scheme fallback), while responses, event contracts, and JS contracts flow back over the same bridge. Protocol v2 addresses all traffic by contract/member and verifies generated core and app manifest fingerprints when the WebView starts.
See Architecture for the full diagram and host model, and Interop Protocol for the wire format.
Native modules are plain C# classes; event and JS contracts are C# interfaces. Their payload/result records define every safe cross-language operation:
[BridgeModule("filesystem")]
public sealed class FileSystemModule : BridgeModuleBase
{
[BridgeMethod("readText")]
public async Task<ReadTextResult> ReadTextAsync(ReadTextArgs args, CancellationToken ct)
=> new(await File.ReadAllTextAsync(args.Path, ct));
}
[JsContract("counter")]
public interface ICounterJs
{
[JsMethod("increment")]
Task<int> IncrementAsync();
}During compilation, a Roslyn generator emits AOT-safe event tokens, Bridge.Js() clients,
codecs, and diagnostics. After compilation, vidra-codegen emits matching TypeScript
native/event proxies and JS-handler registries:
import { filesystem } from "@vidra-dev/sdk";
import { counterHandlers } from "./generated/index.js";
// `path` is required and typo-checked; `content` is inferred as `string`.
const { content } = await filesystem.readText({ path: "/tmp/notes.txt" });
counterHandlers.increment(() => 1);Full pipeline and C# → TS type mapping: Type safety & codegen →
| Page | What's inside |
|---|---|
| Architecture | Host model, bridge design, and the codegen pipeline |
| Interop Protocol | JSON envelopes, transports, and error codes |
| Capabilities | Every built-in module and its typed methods |
| Testing | How the bridge and codegen output are tested |
| Package | Description |
|---|---|
create-vidra-app |
Scaffolder + the vidra CLI (dev / run / build / doctor) |
@vidra-dev/sdk |
Framework-agnostic runtime plus generated native/event proxies |
| Platform | Status |
|---|---|
| Windows | Supported |
| macOS | Supported |
Mobile (iOS / Android) is on the roadmap as the .NET MAUI foundation makes it a natural next target.
Contributions are welcome! To get started:
- Fork the repo and create a branch from
main. - Make your change — for anything non-trivial, consider opening an issue first to discuss the approach.
- Run the relevant test suites (see Testing for how to run them locally).
- Open a pull request with a short description of what changed and why.
