# How Native SDK Renders UI Without WebView Using Its Canvas Engine

> Discover how the Native SDK renders UI without WebView using its canvas engine by compiling markup into GPU commands for direct native execution.

- Repository: [Vercel Labs/native](https://github.com/vercel-labs/native)
- Tags: internals
- Published: 2026-07-18

---

**Native SDK bypasses WebView entirely by compiling declarative markup into GPU command lists that are executed directly on native surfaces via a dedicated canvas runtime.**

The Vercel Native SDK provides a fully-featured, GPU-accelerated UI framework that eliminates the need for embedded browsers. Unlike hybrid frameworks that rely on WebView wrappers, this engine—found in the `vercel-labs/native` repository—processes `.native` markup files through a Zig-based compiler to generate direct GPU draw commands.

## The Canvas Architecture: A WebView-Free Rendering Pipeline

At the heart of Native SDK’s rendering strategy is the **Canvas subsystem** located under `src/primitives/canvas`. This architecture replaces the traditional browser rendering stack with a declarative-to-GPU pipeline that operates entirely in native code.

### Declarative Markup and Strict Type Contracts

UI is defined using `.native` files containing a declarative markup language that resembles HTML but compiles to native draw primitives. The **markup compiler** enforces type safety through a user-defined `Model`/`Msg` contract, ensuring that compile-time checks prevent runtime UI errors before the app launches.

```zig
// Define the contract
pub const Model = struct { counter: usize };
pub const Msg = enum { inc, dec };

// app.native markup
const markup = \\ 
    <column>
        <text>{model.counter}</text>
        <button on-click="inc">+</button>
    </column>;

```

### GPU Surface Views vs. WebView

The platform creates views of kind **`.gpu_surface`** rather than `.webview`. According to `src/runtime/view.zig`, the `RuntimeView` struct initializes a native window surface that connects directly to Metal, Vulkan, or other platform GPU APIs—never instantiating an embedded browser engine.

## Step-by-Step: From Markup to Pixels

The rendering pipeline follows a strict five-stage process that keeps all execution in native code:

1. **Markup Parsing** – `src/primitives/canvas/ui_markup.zig` parses `.native` files into a tree of `canvas.Widget` nodes.
2. **Display List Compilation** – The `canvas.CompiledMarkupImports` generator (around line 3405 in `ui_markup.zig`) converts the widget tree into a `canvas.DisplayList` containing encoded draw commands, gradients, and text layouts.
3. **View Instantiation** – `src/runtime/view.zig` creates a `RuntimeView` configured as a `gpu_surface` and wires it to the canvas runtime via `view_canvas.zig`.
4. **GPU Submission** – `src/runtime/view_canvas.zig` streams the display list into a `canvas.Builder` command buffer and submits it to the platform’s GPU surface APIs.
5. **Interaction Handling** – Input events route directly to the `CanvasWidgetRuntime` in `src/runtime/canvas_widget_runtime.zig`, which rebuilds the display list without JavaScript or HTML reflows.

## Core Components of the Canvas Runtime

The engine’s self-contained architecture relies on these key components:

- **`src/primitives/canvas/ui_markup.zig`** – Parses markup into `MarkupNode` structures and handles compile-time contract verification.
- **`canvas.CompiledMarkupImports`** – Transforms parsed markup into optimized `canvas.DisplayList` objects ready for GPU execution.
- **`src/runtime/view_canvas.zig`** – Manages display-list resources, command copying, and GPU submission buffers.
- **`src/runtime/canvas_widget_runtime.zig`** – Executes widget layout algorithms, maintains state, and handles animation blending entirely in native code.
- **`src/platform/*`** – Provides platform-specific surface backends (Metal, Vulkan, etc.) that the canvas draws into.

Because the engine owns its own **layout engine** (`canvas.widgetLayout*` modules) and rasterizer, it achieves lower latency and a smaller binary footprint than WebView-based alternatives.

## Building a Native UI: Complete Code Example

The following Zig implementation demonstrates how to initialize a WebView-free canvas application:

```zig
const canvas = @import("native_sdk").canvas;
const std = @import("std");

pub const Model = struct { counter: usize };
pub const Msg = enum { inc, dec };

const markup = \\ 
    <column>
        <text>{model.counter}</text>
        <button on-click="inc">+</button>
        <button on-click="dec">-</button>
    </column>;

pub fn main() void {
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();

    // Initialize UI builder with type-safe message handling
    var ui = canvas.Ui(Msg).init(arena.allocator());
    
    // Compile markup against Model/Msg contract
    const compiled = canvas.CompiledMarkupImports(Model, Msg, "app.native", &.{});
    ui.build(compiled) catch |e| std.debug.print("UI build error: {any}\n", .{e});

    // Create native gpu_surface view (no webview involved)
    const view = canvas.AppUi.init(&ui);
    view.run(); // Starts platform event loop
}

```

In this implementation, `canvas.Ui(Msg)` exposes the interface defined in `src/runtime/view.zig`, while `canvas.AppUi` creates the `gpu_surface` view that bypasses browser engines entirely.

## Summary

- Native SDK uses a **Canvas engine** located in `src/primitives/canvas` to render UI without WebView dependencies.
- The **markup compiler** transforms `.native` files into GPU display lists via strict `Model`/`Msg` contracts.
- Views are created as **`gpu_surface`** types in `src/runtime/view.zig`, connecting directly to platform GPU APIs rather than browser engines.
- The **render loop** in `src/runtime/view_canvas.zig` streams display lists into command buffers for Metal or Vulkan submission.
- All layout, rasterization, and animation occur in native code through `src/runtime/canvas_widget_runtime.zig`, eliminating HTML, CSS, and JavaScript overhead.

## Frequently Asked Questions

### How does the Canvas engine compare to WebView rendering performance?

The Canvas engine eliminates the overhead of HTML parsing, CSS styling, and JavaScript execution by compiling markup directly to GPU draw commands. Because `src/runtime/canvas_widget_runtime.zig` handles layout and rasterization in native Zig code rather than through a browser’s render thread, apps achieve lower latency and consistent 60fps animations without the memory bloat of embedded WebViews.

### What platforms does the GPU surface support?

According to the platform bridge in `src/platform/*`, the canvas runtime abstracts over native graphics APIs including Metal on macOS/iOS and Vulkan on Android and Linux. The `gpu_surface` view kind in `src/runtime/view.zig` automatically selects the appropriate backend during `RuntimeView` initialization, ensuring optimal hardware acceleration across supported targets.

### Why does Native SDK use a custom markup language instead of standard HTML?

The `.native` markup language enforces compile-time type safety against the `Model`/`Msg` contract defined in `src/primitives/canvas/ui_markup.zig`. Unlike HTML, which requires runtime interpretation and error handling, this declarative syntax is parsed into `canvas.Widget` nodes that are guaranteed to match the application’s state structure, preventing runtime UI crashes and enabling aggressive compile-time optimizations.

### Can the Canvas engine handle complex animations and accessibility?

Yes. The `canvas_widget_runtime.zig` module implements **retained-packet rendering** and **animation blending** natively, while the display list format in `src/runtime/view_canvas.zig` supports accessibility semantic annotations. Because the engine owns the entire rendering stack from layout to pixel submission, it can implement smooth transitions and screen reader support without deferring to browser implementation quirks.