stacksjs/zyte
Performant, native desktop & mobile apps with web languages. Powered by Zig.
Build native desktop & mobile apps with web technologies, powered by Zig
Zyte is a lightweight, high-performance cross-platform application framework. Create native apps that work on macOS, Linux, Windows, iOS, and Android with web technologies - all with a tiny 1.4MB binary and blazing fast <100ms startup time.
Version 0.0.1 | 31 Native Components | 1.4MB Binary | Cross-Platform | Production-Ready
iOS Integration
Android Integration
Input Components
Display Components
Layout Components
Data Components
Navigation Components
Advanced Components
Build native menubar/system tray apps with full platform support:
Features
Platform Implementations
Rendering Pipeline
Effects & Post-Processing
Advanced Features
Notifications
Clipboard
File Dialogs
System Info
Power Management
Screen Management
URL Handling
Build desktop apps with TypeScript - zero dependencies, just pure Node.js APIs:
# Install the TypeScript SDK
bun add ts-zyte
// app.ts
import { show } from 'ts-zyte'
const html = `
<!DOCTYPE html>
<html>
<head>
<style>
body {
margin: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
font-family: system-ui;
}
</style>
</head>
<body>
<h1>⥠My First Zyte App</h1>
</body>
</html>
`
// That's it! One line to show your desktop app
await show(html, { title: 'My App', width: 800, height: 600 })
# Run it
bun run app.ts
See TypeScript SDK Documentation for the full API.
For advanced use cases where you need maximum performance and control:
# Install via npm
npm install -g ts-zyte
# Or with Bun
bun add -g ts-zyte
# Clone the repository
git clone https://github.com/stacksjs/zyte.git
cd zyte
# Install Zig 0.15.1
# macOS
brew install zig
# Linux
wget https://ziglang.org/download/0.15.1/zig-linux-x86_64-0.15.1.tar.xz
tar -xf zig-linux-x86_64-0.15.1.tar.xz
# Build
zig build
# Run
./zig-out/bin/zyte-minimal http://localhost:3000
Linux:
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev
Windows:
winget install Microsoft.EdgeWebView2Runtime
const std = @import("std");
const zyte = @import("zyte");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var app = zyte.App.init(allocator);
defer app.deinit();
_ = try app.createWindowWithURL(
"My App",
1200,
800,
"http://localhost:3000",
.{
.dark_mode = true,
.hot_reload = true,
},
);
try app.run();
}
const menubar = @import("menubar.zig");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
// Create menu
var menu = try menubar.Menu.init(allocator);
defer menu.deinit();
const show_item = menubar.MenuItem.init(allocator, "Show Window", showWindow);
try menu.addItem(show_item);
try menu.addSeparator();
const quit_item = menubar.MenuItem.init(allocator, "Quit", quit);
try menu.addItem(quit_item);
// Create menubar app
var app = try menubar.MenubarBuilder.new(allocator, "My App")
.icon("icon.png")
.tooltip("My Menubar App")
.menu(menu)
.build();
defer app.deinit();
try app.show();
}
fn showWindow() void {
std.debug.print("Show window\n", .{});
}
fn quit() void {
std.process.exit(0);
}
const mobile = @import("mobile.zig");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
const config = mobile.iOS.AppConfig{
.bundle_id = "com.example.myapp",
.display_name = "My App",
.supported_orientations = &[_]mobile.Orientation{
.portrait,
.landscape_left,
.landscape_right,
},
.status_bar_style = .light,
};
const webview_config = mobile.iOS.WebViewConfig{
.url = "http://localhost:3000",
.enable_javascript = true,
.enable_devtools = true,
};
const webview = try mobile.iOS.createWebView(allocator, webview_config);
defer mobile.iOS.destroyWebView(webview);
// Request permissions
try mobile.iOS.requestPermission(.camera, onPermissionGranted);
// Setup haptic feedback
mobile.iOS.triggerHaptic(.success);
}
fn onPermissionGranted(granted: bool) void {
if (granted) {
std.debug.print("Permission granted!\n", .{});
}
}
const components = @import("components.zig");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
// Create a button
var button = try components.Button.init(allocator, "Click Me", onClick);
defer button.deinit();
button.setEnabled(true);
// Create a date picker
var date_picker = try components.DatePicker.init(allocator, onDateChange);
defer date_picker.deinit();
// Create a tree view
var tree = try components.TreeView.init(allocator);
defer tree.deinit();
var root = try tree.createNode("Root");
var child1 = try tree.createNode("Child 1");
var child2 = try tree.createNode("Child 2");
try root.addChild(child1);
try root.addChild(child2);
try tree.setRoot(root);
// Create a rating component
var rating = try components.Rating.init(allocator, 5, onRatingChange);
defer rating.deinit();
rating.setRating(4.5);
}
fn onClick() void {
std.debug.print("Button clicked!\n", .{});
}
fn onDateChange(year: i32, month: u8, day: u8) void {
std.debug.print("Date: {}-{}-{}\n", .{ year, month, day });
}
fn onRatingChange(rating: f32) void {
std.debug.print("Rating: {d:.1}\n", .{rating});
}
const system = @import("system.zig");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
// Show notification
const notification = system.Notification.init("Hello", "Welcome to Zyte!");
try notification.show();
// Clipboard operations
var clipboard = try system.Clipboard.init(allocator);
defer clipboard.deinit();
try clipboard.setText("Hello, clipboard!");
if (try clipboard.getText()) |text| {
std.debug.print("Clipboard: {s}\n", .{text});
}
// File dialog
const file_dialog = system.FileDialog{
.title = "Open File",
.filters = &[_]system.FileFilter{
.{ .name = "Text Files", .extensions = &[_][]const u8{".txt"} },
},
};
if (try file_dialog.openFile()) |path| {
std.debug.print("Selected: {s}\n", .{path});
}
// System info
const info = try system.SystemInfo.get(allocator);
defer info.deinit();
std.debug.print("OS: {s} {s}\n", .{ info.os_name, info.os_version });
std.debug.print("CPU: {s} ({} cores)\n", .{ info.cpu_brand, info.cpu_cores });
std.debug.print("RAM: {} MB\n", .{info.total_memory / 1024 / 1024});
// Battery status
const battery = try system.PowerManagement.getBatteryInfo();
std.debug.print("Battery: {}% (charging: {})\n", .{ battery.level, battery.is_charging });
}
const gpu = @import("gpu.zig");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
// Initialize GPU context
var ctx = try gpu.Context.init(allocator, .vulkan);
defer ctx.deinit();
// Create render pipeline
var pipeline = try gpu.RenderPipeline.init(allocator, &ctx);
defer pipeline.deinit();
// Create shader
const vertex_shader = try pipeline.createShader(.{
.vertex = "path/to/vertex.glsl",
});
// Create mesh
const vertices = [_]f32{ /* vertex data */ };
var mesh = try gpu.Mesh.init(allocator, &vertices, null);
defer mesh.deinit();
// Apply post-processing
var post_processor = try gpu.PostProcessor.init(allocator, &ctx);
defer post_processor.deinit();
try post_processor.addEffect(.bloom, .{ .intensity = 0.5 });
try post_processor.addEffect(.anti_aliasing, .{ .samples = 4 });
// Render
try pipeline.render(&[_]gpu.RenderCommand{
.{ .draw_mesh = mesh },
});
}
# Launch a local development server
zyte http://localhost:3000
# With custom options
zyte http://localhost:3000 \
--title "My App" \
--width 1200 \
--height 800 \
--dark \
--hot-reload
# Frameless transparent window
zyte http://localhost:3000 \
--frameless \
--transparent \
--always-on-top
Metric | Zyte | Electron | Tauri |
---|---|---|---|
Binary Size | 1.4MB | ~150MB | ~2MB |
Memory (idle) | ~92MB | ~200MB | ~80MB |
Startup Time | <100ms | ~1000ms | ~100ms |
CPU (idle) | <1% | ~4% | <1% |
Platforms | 5 (Desktop + Mobile) | 3 | 3 |
Native Components | 31 | 0 | Limited |
Platform | Status | WebView | Native Components |
---|---|---|---|
macOS | â Production | WKWebView | â All 31 |
Linux | â Production | WebKit2GTK 4.0+ | â All 31 |
Windows | â Production | WebView2 (Edge) | â All 31 |
iOS | â Beta | WKWebView | â UIKit |
Android | â Beta | WebView | â Material |
Zyte is built with a modular architecture:
src/
âââ api.zig # Core API with Result types, builders
âââ mobile.zig # iOS & Android platform support
âââ menubar.zig # Menubar/system tray apps
âââ components.zig # 31 native UI components
âââ gpu.zig # Advanced GPU rendering
âââ system.zig # System integration (notifications, clipboard, etc.)
âââ window.zig # Window management
âââ ipc.zig # Inter-process communication
âââ state.zig # Reactive state management
âââ animation.zig # Animation engine
We welcome contributions! Please see CONTRIBUTING.md for details.
For help, discussion about best practices, or any other conversation:
Zyte is free software, but we'd love to receive a postcard from where you're using it! We showcase them on our website.
Our address: Stacks.js, 12665 Village Ln #2306, Playa Vista, CA 90094, United States ð
We would like to extend our thanks to the following sponsors for funding Stacks development:
The MIT License (MIT). Please see LICENSE for more information.
Made with ð