Craft is a lightweight, high-performance cross-platform application framework. Create native apps that work on macOS, Linux, Windows, iOS, and Android with web technologies — with a small Zig-built binary and a fast cold start. Reproducible numbers live in benchmarks/.
- 🖥️ Desktop — macOS, Linux, Windows
- 📱 Mobile — iOS and Android, with a default web renderer and an opt-in native STX renderer
- 🪟 Menubar Apps — Native system tray/menubar integration
- ⚡ Native Performance — sub-second cold start and small idle footprint; see
benchmarks/for the latest numbers - 🪶 Compact Binary — small enough to embed; sized in CI under
.github/workflows/binary-size.yml - 🔧 Zig-Powered — Built with Zig for maximum performance
Choose the renderer explicitly. The default web mode hosts web content in
WKWebView on iOS or WebView on Android. Experimental native mode runs the
compiled STX route bundle without a browser view: JavaScriptCore and UIKit on
iOS, JavaScriptSandbox and Android views on Android. Both native targets can
compile the same STX components; the browser target renders those components as
HTML. See Native STX for supported components,
capabilities, configuration and verification.
Native screens and their imported code ship in the app. Updating them requires a new app build and release. A server deployment can update API responses and web pages, but does not replace an installed native bundle.
-
iOS Integration
- WKWebView with JavaScript bridge in web mode
- JavaScriptCore and UIKit screen host in native mode
- UIKit native components
- Haptic feedback (light, medium, heavy, selection, success, warning, error)
- Device permissions (camera, location, notifications, photos, contacts, microphone)
- Orientation support (portrait, landscape)
- Status bar control
- App lifecycle management
-
Android Integration
- WebView with JavaScript interface in web mode
- JavaScriptSandbox and Android screen host in native mode
- Activity lifecycle
- Permissions system
- Vibration and haptic feedback
- File access and storage
- Material Design support
- Button, TextInput, Checkbox, RadioButton
- Slider (with snapping, labels, percentage), ColorPicker (RGB/HSL/Hex), DatePicker, TimePicker
- Autocomplete (fuzzy search, keyboard nav)
- Label, ImageView, ProgressBar, Spinner
- Avatar, Badge, Chip, Card
- Tooltip (8 positions, 6 themes), Toast (notifications)
- ScrollView, SplitView, Accordion, Stepper
- Modal, Tabs, Dropdown
- ListView, Table, TreeView, DataGrid
- Chart (line, bar, pie)
- TabView, Menu, Toolbar, StatusBar
- Rating (star ratings with half-star support)
- CodeEditor (syntax highlighting)
- MediaPlayer (video/audio)
Build native menubar/system tray apps with full platform support:
- Native system tray icons
- Custom menus with shortcuts
- Tooltip support
- Click handlers (left, right, double, middle)
- Window attachment for popover-style UIs
- Notifications integration
- macOS: NSStatusBar integration
- Linux: AppIndicator/StatusNotifier
- Windows: System tray via Shell_NotifyIcon
- Multi-backend support (Vulkan, Metal, Direct3D)
- Shader management (vertex, fragment, compute)
- Buffer management (vertex, index, uniform, storage)
- Texture and render target support
- Mesh rendering with vertex data
- 10 built-in effects: Bloom, Blur, Sharpen, Vignette
- Chromatic Aberration, Film Grain, Color Grading
- Tone Mapping, Anti-Aliasing, Ambient Occlusion
- Compute shader support
- Ray tracing with acceleration structures
- Multi-GPU support
- GPU profiling and performance monitoring
- Native OS notifications
- Custom titles, bodies, icons
- Action buttons
- Urgency levels (low, normal, critical)
- Click callbacks
- Text read/write
- Image support
- File paths
- Watch for changes
- Open file/multiple files
- Save file
- Select directory
- Custom file type filters
- Default paths
- OS name and version
- CPU information
- Memory stats (total, available, used)
- System uptime
- Battery status and level
- Charging state
- Prevent/allow sleep
- Power state monitoring
- Multi-monitor support
- Screen resolution and scaling
- Primary screen detection
- Screen positioning
- Open URLs in default browser
- Register custom URL schemes
- Deep linking support
- 📟 Powerful CLI - 20+ command-line flags for quick prototyping
- 🔍 DevTools - Built-in WebKit DevTools (right-click > Inspect Element)
- 🎨 Custom Styles - Frameless, transparent, always-on-top windows
- 🌉 JavaScript Bridge - Seamless communication between JS and native code
- ⚙️ Configuration - TOML-based config files
- 📊 Logging - Structured logging system
- 🔥 Hot Reload - File watching with state preservation (scroll, forms, focus, custom state)
- 🎯 Error Handling - Context-aware errors with stack traces, metadata, recovery strategies
- 📈 Performance Monitoring - Built-in benchmarking suite, memory tracking, FPS monitoring
- 🐛 Enhanced Debugging - Beautiful error overlays, color-coded output, actionable suggestions
- 📏 Position Control - Precise window positioning (x, y)
- 🖥️ Fullscreen - Native fullscreen mode
- 🔄 State Management - Minimize, maximize, close, hide, show
↔️ Resize Control - Custom resize behavior- 🪟 Multi-Window - Multiple window support
- 🖥️ Multi-Monitor - Multi-monitor awareness
- 📡 Advanced IPC - Message passing, channels, RPC, shared memory
- 🖌️ Native Rendering - Canvas API, pixel manipulation
- ⌨️ Enhanced Shortcuts - 90+ key codes with modifiers
- ♿ Accessibility - WCAG 2.1 AAA compliance with 40+ ARIA roles
- Focus management with trap support
- Keyboard navigation system (Tab, Arrow, Home, End keys)
- Screen reader announcements (4 priority levels)
- Contrast ratio checker (validates AA/AAA standards)
- Semantic HTML mapping
- 69 comprehensive accessibility tests
- 🌓 Advanced Theming - Nord, Dracula, Gruvbox, custom themes
- 💨 Performance - LRU caching, object pooling, lazy loading, memoization
- 📊 Monitoring - Built-in performance profiling and benchmarking
- Statistical analysis (mean, median, std dev, ops/sec)
- Memory allocation tracking
- JSON and text report generation
- 🔄 Async/Await - Non-blocking I/O, streaming, promises, channels
- 🧩 WebAssembly - WASM plugin system with sandboxing
- 🎬 Animations - 31 easing functions, keyframes, springs
- 🔄 State Management - Reactive state with observers, undo/redo
- 🔌 Plugin System - Dynamic library loading with marketplace
- 🛡️ Sandbox - 7 permission types for security
- 🌐 i18n - Internationalization with RTL support
- 🔐 Code Signing - macOS, Windows, Linux
- 📦 Installers - DMG, PKG, MSI, DEB, RPM, AppImage
- 🔄 Auto-Updater - Built-in update mechanism
- 📸 Screen Capture - Screenshots and recording
- 🖨️ Print Support - Native printing
- 📥 Downloads - Download management
- 🔌 WebSocket - Real-time communication
- 🔗 Custom Protocols - Register custom URL handlers (craft://)
- 🎯 Drag & Drop - File drag and drop support
The fastest way to get started is with create-craft:
# Create a new Craft app
bun create craft my-app
# Navigate to your app
cd my-app
# Start development
bun run devChoose from multiple templates:
- minimal - Simplest possible app
- full-featured - Modern styled app with examples
- todo-app - Interactive todo list
# Use a specific template
bun create craft my-app --template full-featuredSee create-craft documentation for all options.
Explore ready-to-run examples in the examples/ directory:
# Simple system tray app
bun run examples/simple-tray.ts
# Full-featured system tray app
bun run examples/system-tray-app.ts
# Pomodoro timer with live menubar updates (recommended!)
bun run examples/pomodoro.ts
# Alternative Pomodoro timer
bun run examples/menubar-timer.tsSee examples documentation for more details.
Build desktop apps with TypeScript - zero dependencies, just pure Node.js APIs:
# Install the TypeScript SDK
bun add craft-native// app.ts
import { show } from 'craft-native'
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 Craft 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.tsSee TypeScript SDK Documentation for the full API.
For advanced use cases where you need maximum performance and control:
# Install with Bun
bun add -g craft-native# Clone the repository
git clone https://github.com/home-lang/craft.git
cd craft
# Install pantry dependencies
pantry install
# Optional for interactive shells
eval "$(pantry env)"
# Build
bun run build:core
# Run
./packages/zig/zig-out/bin/craft http://localhost:3000Linux:
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-devWindows:
winget install Microsoft.EdgeWebView2Runtimeconst std = @import("std");
const craft = @import("craft");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var app = craft.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 Craft!");
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
craft http://localhost:3000
# With custom options
craft http://localhost:3000 \
--title "My App" \
--width 1200 \
--height 800 \
--dark \
--hot-reload
# Frameless transparent window
craft http://localhost:3000 \
--frameless \
--transparent \
--always-on-topCraft uses a flexible package configuration system that supports multiple formats:
Craft automatically searches for configuration files in the following order:
craft.toml(TOML format)craft.json(JSON format)package.jsonc(JSON with comments)package.json(standard JSON, undocumented but supported)
[package]
name = "my-craft-app"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
description = "A cross-platform desktop application"
license = "MIT"
[dependencies]
# Local path dependency
craft-ui = { path = "../craft-ui" }
# Git dependency
craft-http = { git = "https://github.com/user/craft-http.git" }
# Version dependency (from registry)
craft-database = { version = "^1.2.0" }
[workspaces]
packages = ["packages/_", "apps/_"]
[scripts]
dev = "zig build run"
test = "zig build test"
build = "zig build -Doptimize=ReleaseFast"
format = "find src -name '_.zig' -exec zig fmt {} +"{
"name": "my-craft-app",
"version": "0.1.0",
"authors": ["Your Name <you@example.com>"],
"description": "A cross-platform desktop application",
"license": "MIT",
"dependencies": {
"craft-ui": { "path": "../craft-ui" },
"craft-http": { "git": "https://github.com/user/craft-http.git" },
"craft-database": "^1.2.0"
},
"workspaces": {
"packages": ["packages/_", "apps/_"]
},
"scripts": {
"dev": "zig build run",
"test": "zig build test",
"build": "zig build -Doptimize=ReleaseFast",
"format": "find src -name '_.zig' -exec zig fmt {} +"
}
}[dependencies]
my-lib = { path = "../my-lib" }{
"dependencies": {
"my-lib": { "path": "../my-lib" }
}
}[dependencies]
awesome-lib = { git = "https://github.com/user/awesome-lib.git" }{
"dependencies": {
"awesome-lib": { "git": "https://github.com/user/awesome-lib.git" }
}
}[dependencies]
popular-lib = { version = "^1.0.0" }
# Or shorthand
another-lib = "^2.0.0"{
"dependencies": {
"popular-lib": "^1.0.0"
}
}Organize multiple packages in a monorepo structure:
[package]
name = "my-workspace"
version = "0.1.0"
[workspaces]
packages = [
"packages/core",
"packages/ui",
"packages/cli",
"apps/_"
]
[scripts]
build = "zig build"
test = "zig build test"[package]
name = "core"
version = "0.1.0"
description = "Core functionality"
[dependencies]
some-lib = "^1.0.0"Define custom commands for common tasks:
[scripts]
dev = "zig build run -Doptimize=Debug"
build = "zig build -Doptimize=ReleaseFast"
test = "zig build test"
bench = "zig build bench"
format = "zig fmt src/"
lint = "zig build check"Run scripts with: craft run <script-name> (planned feature)
const std = @import("std");
const package = @import("package.zig");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Load package from current directory
const pkg = try package.findAndLoadPackage(allocator, ".");
defer pkg.deinit(allocator);
std.debug.print("Package: {s} v{s}\n", .{ pkg.name, pkg.version });
if (pkg.dependencies) |deps| {
var it = deps.iterator();
while (it.next()) |entry| {
std.debug.print(" - {s}\n", .{entry.key_ptr._});
}
}
}{
"name": "my-desktop-app",
"version": "1.0.0",
"description": "A beautiful desktop application",
"authors": ["Your Name"],
"license": "MIT",
"dependencies": {
"craft-ui": "^1.0.0",
"craft-notifications": "^0.5.0"
},
"scripts": {
"dev": "zig build run",
"build:mac": "zig build -Dtarget=aarch64-macos",
"build:linux": "zig build -Dtarget=x86_64-linux",
"build:windows": "zig build -Dtarget=x86_64-windows"
}
}[package]
name = "menubar-timer"
version = "0.2.0"
description = "A simple menubar timer"
license = "MIT"
[dependencies]
craft-menubar = { version = "^1.0.0" }
craft-notifications = { version = "^0.5.0" }
[scripts]
dev = "zig build run"
build = "zig build -Doptimize=ReleaseFast"{
"name": "craft-database",
"version": "1.2.0",
"description": "SQL database access with SQLite driver",
"authors": ["Craft Contributors"],
"license": "MIT",
"dependencies": {
"craft-diagnostics": { "path": "../diagnostics" }
},
"scripts": {
"test": "zig test src/database.zig",
"bench": "zig build bench"
}
}Craft 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 # 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
└── package.zig # Package configuration and management
Benchmarked on Apple M3 Pro with Hello World apps. See benchmarks/ for full methodology.
| Metric | Craft | Tauri | React Native | Electrobun | Electron |
|---|---|---|---|---|---|
| Binary Size | 297 KB | 7.69 MB | 20.65 MB | 131 KB* | 392.37 MB |
| Startup (p50) | 168 ms | 259 ms | 243 ms | 246 ms | 412 ms |
| Memory (RSS) | 86 MB | 106 MB | 109 MB | 148 MB | 369 MB |
| IPC (single msg) | 532 ns | 778 ns | — | 760 ns | 837 ns |
*Electrobun binary is 131 KB but distributable is 60.12 MB
Craft is 1351xsmaller than Electron,2.4xfaster to start, and uses4.3x less memory.
| Platform | Status | WebView | Native Components |
|---|---|---|---|
| macOS | ✅ Production | WKWebView | ✅ All 35 |
| Linux | 🧪 Experimental | WebKit2GTK 4.1 / GTK3 | Partial |
| Windows | 🧪 Experimental | WebView2 (Edge) | Partial |
| iOS | 🚧 Preview | WKWebView | Partial |
| Android | 🚧 Preview | WebView | Partial |
- 📖 API Reference - Complete API documentation
- 🚀 Quick Start - Get started quickly
- 📘 Getting Started - Detailed guide
- ✨ Features - Complete feature list
- 🤝 Contributing - Contribution guide
- 📦 Releasing - Publication gates and recovery
- 📋 Changelog - Release history
We welcome contributions! Please see CONTRIBUTING.md for details.
For help, discussion about best practices, or any other conversation:
Craft 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 💙
