Skip to main content

Core Concepts

Understanding Zylix’s core concepts is essential for building efficient cross-platform applications. This section covers the fundamental building blocks that power every Zylix app.

Terms
#

  • State: The application data owned by Zig.
  • Event: A typed action dispatched from platform UI.
  • VNode: A node in the virtual UI tree.
  • Diff: The computed set of changes between UI trees.

Concept
#

Zylix is built on several key principles:

  1. Zig Owns the Logic: All application state and business logic lives in Zig. Platform shells are thin wrappers that handle rendering and user input.

  2. Unidirectional Data Flow: State changes flow in one direction: Event → State → Virtual DOM → Patches → UI.

  3. Immutable State Transitions: State is never mutated directly. Instead, events trigger state transitions that create new state versions.

  4. Minimal Patches: The diffing algorithm ensures only necessary changes are applied to the UI.

Implementation
#

Core Components
#

Data Flow Diagram
#

flowchart TB
    User["👆 User Interaction<br/>(tap, click, type, scroll)"]

    subgraph Shell1["Platform Shell (Swift, Kotlin, JS, C#, C)"]
        Convert["Converts native events to Zylix events"]
        Call["Calls: zylix_dispatch(event_type, payload)"]
    end

    subgraph Dispatcher["Event Dispatcher"]
        Route["Routes event to appropriate handler"]
        Validate["Validates event payload"]
    end

    subgraph Store["State Store"]
        Transition["Applies state transition"]
        Version["Increments version number"]
        Trigger["Triggers re-render"]
    end

    subgraph Builder["Virtual DOM Builder"]
        Construct["Constructs new VNode tree from state"]
        Render["Uses component render functions"]
    end

    subgraph Differ["Diff Algorithm"]
        Compare["Compares old tree vs new tree"]
        Generate["Generates minimal patch set"]
    end

    subgraph Shell2["Platform Shell"]
        Receive["Receives patches via zylix_get_patches()"]
        Apply["Applies patches to native UI elements"]
    end

    Updated["✨ UI Updated<br/>User sees changes"]

    User --> Shell1
    Shell1 --> Dispatcher
    Dispatcher --> Store
    Store --> Builder
    Builder --> Differ
    Differ --> Shell2
    Shell2 --> Updated

Quick Reference
#

ConceptFilePurpose
Virtual DOMvdom.zigLightweight tree representation of UI
Diff Algorithmdiff.zigComputes minimal changes between trees
State Storestore.zigGeneric state container with versioning
Statestate.zigApplication-specific state definitions
Componentscomponent.zigUI building blocks with props and events
Eventsevents.zigType-safe event definitions
Arenaarena.zigEfficient memory allocation
ABIabi.zigC-compatible function exports
WASMwasm.zigWebAssembly-specific bindings

Memory Model
#

Zylix uses arena allocation for predictable, GC-free performance:

// Arena allocates from a contiguous buffer
var arena = Arena(4096).init();

// Allocations are O(1) bump pointer operations
const node = arena.alloc(VNode);

// Reset frees all allocations at once
arena.reset();

Benefits:

  • No GC pauses: Deterministic deallocation
  • Cache-friendly: Contiguous memory layout
  • Fast allocation: O(1) bump allocation
  • Bulk deallocation: Reset entire arena instantly

Type Safety
#

Zylix leverages Zig’s compile-time features for safety:

// Discriminated unions prevent invalid states
pub const Event = union(enum) {
    todo_add: []const u8,
    todo_toggle: u32,
    todo_remove: u32,
};

// Exhaustive switch ensures all cases handled
switch (event) {
    .todo_add => |text| addTodo(text),
    .todo_toggle => |id| toggleTodo(id),
    .todo_remove => |id| removeTodo(id),
}

Platform Bindings
#

Zylix exposes a C ABI for cross-language compatibility:

// Exported functions (abi.zig)
export fn zylix_init() c_int;
export fn zylix_deinit() c_int;
export fn zylix_dispatch(event_type: u32, payload: ?*anyopaque, len: usize) c_int;
export fn zylix_get_state() ?*const State;
export fn zylix_render() c_int;
export fn zylix_get_patches() ?*const DiffResult;

Platform shells call these functions to:

  1. Initialize/deinitialize the framework
  2. Dispatch user events
  3. Query current state
  4. Trigger rendering
  5. Retrieve patches to apply

Pitfalls
#

  • Skipping initialization leads to null state reads.
  • Holding ABI pointers across dispatch can invalidate data.
  • Unstable IDs cause diff mismatches in lists.

Next Steps
#

Virtual DOM

·7 mins
The Virtual DOM (VDOM) is Zylix’s core abstraction for efficient UI updates. Instead of manipulating platform UI elements directly, you describe your UI as a tree of lightweight VNode structures. Zylix then computes the minimal set of changes needed to update the actual UI. Terms # VNode: A node in the virtual UI tree. Diff: The computed set of changes between two trees. Patch: A single update operation applied to the platform UI. Concept # Why Virtual DOM? # Direct UI manipulation is expensive and error-prone:

State Management

·7 mins
Zylix uses centralized, version-tracked state management. All application state lives in Zig and is exposed read-only to platform shells. State changes are atomic, versioned, and trigger automatic re-renders. Terms # State: The full application data owned by Zig. Version: Monotonic counter incremented on each committed change. Diff: A change mask used to update UI efficiently. Store: Generic container that manages current/previous snapshots. Concept # Core Principles # Single Source of Truth: One global state store owns all application data Immutable Updates: State transitions create new state versions Version Tracking: Every change increments a version number Diff Detection: Changes are tracked for efficient rendering Implementation # Application State # pub const AppState = struct { /// Example: Counter value counter: i64 = 0, /// Example: Form input input_text: [256]u8 = [_]u8{0} ** 256, input_len: usize = 0, /// Get view data pointer for ABI pub fn getViewData(self: *const AppState) ?*const anyopaque { return @ptrCast(self); } /// Get view data size for ABI pub fn getViewDataSize(self: *const AppState) usize { return @sizeOf(AppState); } }; UI State # pub const UIState = struct { /// Current screen screen: Screen = .home, /// Loading indicator loading: bool = false, pub const Screen = enum(u32) { home = 0, detail = 1, settings = 2, }; }; Combined State # pub const State = struct { /// State version (monotonically increasing) version: u64 = 0, /// Application-specific state app: AppState = .{}, /// UI state hints ui: UIState = .{}, /// Last error message last_error: ?[]const u8 = null, /// Increment version after state change pub fn bumpVersion(self: *State) void { self.version +%= 1; } }; Generic State Store # The Store provides type-safe state management with automatic versioning:

Components

·7 mins
Components are Zylix’s reusable UI building blocks. They encapsulate structure, styling, and behavior into composable units that can be rendered to any platform. Terms # Component: A declarative UI element (button, text, container). Props: Data that configures the component. State: Interactive flags (hover, focus, checked). Concept # Components are data. The renderer decides how to map them to native UI on each platform.

Event System

·7 mins
Zylix uses a type-safe event system to handle user interactions. Events flow from platform shells through the core, triggering state changes and UI updates. Terms # Event: A typed action sent from platform UI to the Zig core. Dispatch: The ABI call that routes events to handlers. Payload: Optional data attached to an event. Concept # Event Architecture # sequenceDiagram participant User participant Shell as Platform Shell participant Core as Zylix Core (Zig) participant Handler as Event Handler participant State as State Update User->>Shell: Tap button Note over Shell: Convert to Zylix event Shell->>Core: zylix_dispatch(EVENT_TODO_ADD, "Buy groceries", 13) Note over Core: Validate event type Note over Core: Parse payload Note over Core: Route to handler Core->>Handler: Dispatch event Note over Handler: switch (event) {<br/> .todo_add => addTodo(text),<br/> .todo_toggle => toggleTodo(id),<br/>} Handler->>State: Update state Note over State: state.todos[id].completed = true<br/>state.version += 1<br/>scheduleRender() State-->>Core: Result code Core-->>Shell: Return result Implementation # Event Types # Built-in Events # Zylix provides common event types for UI interactions: