Introduction: Why Two Approaches Exist
For years, .NET MAUI on Linux was a gap in the ecosystem. Windows had WinUI, macOS and iOS had Catalyst and UIKit, Android had its own handlers, and Linux had community workarounds. That gap is now being closed from two directions at once, and the two solutions are not different implementations of the same idea. They are different answers to what "native" should mean on a Linux desktop.
Microsoft's answer, still experimental and living in its maui-labs repository, is a GTK4-based backend: MAUI's virtual controls map onto real GTK4 widgets (GtkButton, GtkEntry, GtkGrid), the same strategy MAUI uses on every other platform: wrap the platform's native toolkit.
OpenMaui Linux takes the opposite bet. There is no widget toolkit under it at all. It renders everything with SkiaSharp on the GPU and talks to the display server itself: a hand-written Wayland client and an Xlib client, with GTK reduced to an optional helper for a few desktop dialogs. Every MAUI control (buttons, entries, collection views, Shell, shapes, the WebView) is a Skia* view drawn into one render tree.
Both projects answer the same question, "how does a MAUI app run well on a Linux desktop?", but they optimise for different things: toolkit fidelity versus rendering control, and "looks like a GTK app" versus "looks and behaves like your app on every platform, including Linux". This article is the map: what each approach is, how each is built, and how to decide which one fits.
What is OpenMaui? Skia Rendering and Native Windowing
OpenMaui Linux (OpenMaui.Controls.Linux) treats the operating system as a place to get a window, a GPU surface and a stream of input events. Everything visual is owned by the app.
At the core is SkiaView, an abstract base that mirrors MAUI's VisualElement: it is a BindableObject (so XAML and data binding work on platform views too), it runs its own measure/arrange pass, hit-testing, transforms and drawing through an OnDraw(SKCanvas, SKRect) override. Every MAUI control has a Skia counterpart: SkiaLabel, SkiaButton, SkiaEntry, SkiaGrid, SkiaCollectionView, SkiaTableView, and a full SkiaShell that implements Shell navigation, flyout and tab bar.
// Simplified shape of the pattern used throughout Views/
public abstract partial class SkiaView : BindableObject, IAccessible
{
protected abstract void OnDraw(SKCanvas canvas, SKRect bounds);
protected virtual Size MeasureOverride(Size availableSize) => ...;
protected virtual void ArrangeOverride(Rect bounds) => ...;
}
Handlers (ButtonHandler, EntryHandler, GridHandler, and so on) follow MAUI's standard ViewHandler<TVirtual, TPlatform> pattern with PropertyMapper and CommandMapper dictionaries. From MAUI's perspective nothing is unusual; the difference is entirely on the platform side of the handler. Instead of a Gtk.Button, it creates a SkiaButton that participates in a shared render tree. Because the platform views are ordinary MAUI-shaped objects, MAUI's own machinery drives them: the animation manager and ticker, the VisualStateManager, triggers and behaviors, gesture recognizers, ControlTemplate, FormattedText. OpenMaui does not approximate those; it plugs into them.
Below the render tree sits SkiaRenderingEngine, which tracks dirty regions and presents frames through an IRenderTarget: RasterRenderTarget (CPU pixels via wl_shm or XPutImage) or the EGL targets, where Skia draws with OpenGL ES straight into a window surface and eglSwapBuffers hands the frame to the compositor with no copy. The GPU path is the default; raster is the automatic fallback when EGL is unavailable (software-only drivers, headless CI).
Windowing is native and low-level. WaylandWindow is a Wayland client built directly on libwayland-client through P/Invoke: xdg-shell toplevels, wp_fractional_scale_v1 and wp_viewporter for pixel-exact fractional scaling, zwp_text_input_v3 for IME, wl_data_device for clipboard and drag-and-drop, zwp_primary_selection_v1 for middle-click paste, Skia-drawn client-side decorations where the compositor asks for them. Protocol interface tables the stock library does not export come from a small companion shim, libopenmaui_wl.so. X11Window does the equivalent over Xlib: XDND, _NET_WM_SYNC_REQUEST resize synchronisation, EGL presentation.
GTK is present in OpenMaui, but demoted: it can supply a file chooser, the print options dialog and theme detection when it is installed. It never draws app content, and the platform runs without it.
OpenMaui doesn't ask GTK to draw a button. It asks Skia to draw a rectangle, some text and a pressed state, and tells the compositor where the pixels are.
What is Microsoft MAUI GTK4? Native Widget Mapping
Microsoft's GTK backend, as it describes itself, follows the template MAUI uses everywhere else: a real native toolkit does the rendering and MAUI's handlers are thin adapters translating cross-platform view state into widget properties.
A MAUI Button becomes a Gtk.Button, an Entry a Gtk.Entry, a Grid a Gtk.Grid with constraints computed by the handler. There is no custom drawing code for a button's corners and no custom hit-testing for a slider's thumb; GTK4's CSS-driven theming and its GSK renderer do that.
The consequence is that appearance, animation curves, focus rings and keyboard navigation come from GTK itself. If the user runs a dark GTK theme or a high-contrast theme, the MAUI app picks it up like every other GTK app, because structurally it is one.
The trade-off is depth of control. MAUI features that do not map onto a GTK widget concept (custom VisualStateManager transitions, Shape and Path geometry, GraphicsView, gesture combinations, ControlTemplate) need either GTK's custom-draw escape hatches or a behaviour that differs from iOS, Android and Windows. Where OpenMaui's job is "reproduce MAUI's behaviour exactly, using any pixels it wants", the GTK backend's job is "map MAUI's intent onto whatever GTK4 already knows how to draw".
High-Level Architecture Comparison
Trace the same request, "render a Button inside a Grid", through both stacks.
OpenMaui path:
- MAUI creates the virtual
Button/Gridtree. GridHandlerandButtonHandlercreateSkiaGridandSkiaButtonplatform views and wire the property mappers.SkiaGrid(aSkiaLayoutView) runs MAUI's measure/arrange rules in managed code and positions the button.SkiaButton.OnDrawpaints the rounded rectangle, the text run (throughSkiaFontFactory, with fontconfig-driven fallback) and the pressed state onto anSKCanvas.SkiaRenderingEnginemarks the button's bounds dirty, draws the frame and hands it to the render target.- The render target presents it:
eglSwapBufferson the EGL path,wl_shm/XPutImageon the raster fallback. - Input:
WaylandWindoworX11Windowdecodes a pointer event,WindowContexthit-tests theSkiaViewtree, andGestureManagerdispatches it under MAUI's own gesture-recognizer rules, soClickedfires exactly as it does on other platforms.
GTK4 path:
- MAUI creates the same virtual tree.
- The handlers create a
Gtk.GridandGtk.Buttonand set GTK properties from MAUI values. - GTK's layout manager positions the widgets.
- GSK draws the button with the active GTK CSS theme.
- GTK's application main loop owns compositing and presentation.
- GTK's event controllers deliver a
clickedsignal, and the handler translates it into MAUI'sClicked.
The upshot: OpenMaui owns every layer below the handler (layout, text, painting, windowing, presentation, input) in managed C# and P/Invoke, in one repository. The GTK backend owns only the mapping layer and delegates the rest to GTK's C libraries.
If Microsoft MAUI GTK4 is a native speaker, OpenMaui is a gifted impressionist. It can paint anything, including a convincing GTK, but it is always painting.
Rendering Philosophy: Pixel-Exact Skia vs Native GTK Look-and-Feel
This is where the two diverge most sharply, and where developers feel it first.
OpenMaui's SkiaTheme defines a Material-style palette (light and dark) applied consistently to every control, and SystemThemeService follows the desktop's dark-mode and accent settings through gsettings/dconf. A Button on Linux looks like the Button the OpenMaui team wrote, not like whatever Adwaita or Breeze specifies, and a design system built on iOS or Android translates faithfully because visual states, triggers and styles run through the same MAUI pipeline. The cost is that the app does not inherit GTK theme quirks or desktop-specific widget conventions; it inherits your app's conventions.
The GTK backend inverts the trade-off. A button is drawn by the same GSK/CSS pipeline as every other GTK4 application, so it matches the user's theme and behaves like a GTK app for screen readers through GTK's accessibility bridge. What you give up is cross-platform visual parity: an Entry's focus ring, corner radius or padding on Linux may differ from WinUI or Catalyst, because those pixels are GTK's decision.
One more difference matters in practice: measurability. Because OpenMaui owns the pixels, it can test them. The repository renders every control offscreen through the real engine at 1.0x, 1.25x, 1.5x, 1.75x and 2.0x and compares against committed baselines on every test run, and it generates a compatibility scorecard from that run: 19 categories mirroring the table Microsoft publishes for its own backend, 125 items, each counted only when its mapped tests pass. At the time of writing it reports 125 of 125. A widget-mapping backend can claim parity; it cannot easily prove it pixel by pixel, because the pixels are not its own.
When to Choose Each Approach
Reach for OpenMaui Linux when:
- You need visual and behavioural consistency across platforms: a branded app that should look and act the same on Windows, macOS, iOS, Android and Linux without per-platform styling drift.
- You depend on custom rendering: shapes and paths,
GraphicsView, animation timelines,ControlTemplate,FormattedText, anything that does not map onto a stock widget. - You need protocol-level desktop integration today: native Wayland clipboard and drag-and-drop,
text-input-v3IME, fractional scaling, system tray, CUPS printing, multiple top-level windows. - You need a WebView or Blazor Hybrid that works in native Wayland mode: OpenMaui composites WPE WebKit frames inside its own render tree, on Wayland and X11 alike.
- You want evidence: a generated scorecard and golden screenshots in the repository, regenerated per release.
- You are targeting kiosks, embedded Linux or minimal window managers where a GTK theme is not even meaningful.
Reach for Microsoft's GTK4 backend when:
- Matching the user's GTK theme exactly matters more than matching your other platforms, and your users are on GNOME-family desktops.
- Your app is conventional (forms, lists, navigation) with no custom drawing, and you are comfortable with an experimental backend's current coverage.
- You would rather debug against GTK4's widget behaviour than a rendering pipeline you can step through.
A useful gut-check: if you already ship a MAUI app on other platforms and Linux is one more target, OpenMaui gets you there with no visual drift and a number you can quote. If Linux is your only target and you want a GTK app that happens to be written in MAUI, the GTK backend is the more natural fit, once it is ready.
The choice isn't really "which is better". It is "which failure mode can you live with", and whether you can measure it.
Conclusion
Both projects are solving the decade-old tension in cross-platform UI: paint your own consistent world, or speak the local dialect. WinUI, Catalyst and the Android handlers made the "local dialect" choice for MAUI's other platforms, and the GTK4 backend extends it to Linux. OpenMaui makes the opposite bet: that a single self-rendered engine, talking Wayland and X11 directly, gives more control, more consistency, and access to capabilities (GPU presentation, a composited WebView, hardware video decode, IME and drag-and-drop at the protocol level) that a widget-mapping layer cannot easily expose.
With OpenMaui, failures look like rendering bugs, and the golden tests exist to catch them before a human does. With the GTK backend, failures look like MAUI features that do not map onto GTK widgets, or behaviour that diverges from your other platforms.
The next articles go deeper: setting up an OpenMaui app from dotnet new to first frame, then the handler, rendering and interop layers side by side, and finally how to read the compatibility scorecard and plan a Linux strategy around it. Linux MAUI development in 2026 is not a single road; it is a fork, and each path leads somewhere genuinely different.