Build native GPU-accelerated desktop apps with Solid and TypeScript. Your components render directly to the GPU via Metal, DirectX, or Vulkan — no Electron, no web views.
import { createSignal } from "solid-js"
import { render, View, Text, Button } from "@solo/solid"
function Counter() {
const [count, setCount] = createSignal(0)
return (
<View style={{ display: "flex", gap: 12 }}>
<Text>{count()}</Text>
<Button onClick={() => setCount((v) => v + 1)}>Increment</Button>
</View>
)
}
render(() => <Counter />, { title: "Counter", width: 480, height: 320 })TypeScript / TSX
↓
Solid ← custom renderer (babel-preset-solid, universal mode)
↓
@solo/core ← framework-neutral: mutation protocol types, batching,
↓ event registry, automation client, frame loop
@solo/native ← napi-rs bridge; retained element tree in Rust
↓
GPUI ← Zed's GPU UI framework (pinned revision)
Solo is description-driven, not DOM-like. Frameworks never mutate a
retained JS tree: each commit queues a minimal batch of native mutations
(createElement, appendChild, setText, setStyle, setCustomProp,
setEventListener) that is flushed to Rust through a single applyBatch
FFI call. GPUI's retained tree owns all real state.
| package | role |
|---|---|
@solo/native |
Rust/napi bridge. Retained tree, GPUI element building, text selection, syntax highlighting, markdown, diffs, input editors, automation host, GPU-backed test renderer (macOS). |
@solo/core |
Framework-neutral primitives: protocol/style types, event-handler registry, mutation batching (wrapWithBatching), frame loop, automation client/server, mock renderer for tests. |
@solo/solid |
Solid custom renderer. Maps Solid's universal ops onto the native mutation protocol with fine-grained updates; semantic primitives (View, Text, Button); window render(); automation wiring; test harness. |
- Solid compiles JSX to universal renderer ops (
createElement,insertNode,insert,setProp, …) against@solo/solid/runtime. - Every op maps onto a native mutation and is queued through
wrapWithBatching; exactly one microtask-scheduledapplyBatchflushes per reactive transaction. A signal-driven text change becomes a singlesetText. - Removal is a detach: nodes are only destroyed at commit time if they were not reattached, so keyed list moves preserve subtrees.
- Native events arrive by element ID on the JS thread and dispatch into a
shared handler registry (
handleGpuixEvent).
div, text, img, svg, input, textarea, anchored, code
(Tree-sitter highlighting), diff (unified patches), markdown
(GFM), virtual-list (windowed rows). Styles are a CSS-like subset
(StyleDesc) applied natively, including hover/active pseudo-states,
overflow scrolling, opacity, borders, and text metrics.
Solo ships a Playwright-style automation protocol over stdio (SSE
framing). Any app whose stdin is not a TTY serves it automatically:
tree queries by testId/text/type, click/mouse/keyboard/wheel injection,
programmatic scrolling, bounds, screenshots, clock control. The client
lives in @solo/core/automation; see examples/tasks/diagnose-scroll.mts.
- Headless (all platforms):
MockNativeRendererrecords every mutation op for protocol-level assertions. Seepackages/solid/src/__tests__/lifecycle.test.tsx. - GPU-backed (macOS):
createSolidNativeTestRoot()drives real frames, hit testing, selection, and screenshots throughTestGpuixRenderer.
Run tests:
cd packages/solid && bun run test # vitest
cd packages/native && cargo test --lib && cargo test --test layout_probe| example | demonstrates |
|---|---|
examples/tasks |
full task manager: store state, keyed list, scroll, input, automation regression suite |
examples/solid-counter |
minimal counter window |
Requirements: Bun, Rust 1.97.1 (see rust-toolchain.toml), plus Xcode's
Metal Toolchain on macOS (xcodebuild -downloadComponent MetalToolchain).
bun install
bun run build:native # napi release build (~minutes)
cd examples/tasks && bun run build && node dist/index.jsGPUI comes from a pinned git revision of remorses/zed (branch
gpui-macos-embedded); see docs/gpui-dependency.md.
| platform | windows | input | notes |
|---|---|---|---|
| macOS | ✅ | ✅ incl. synthetic input & GPU test renderer | embedded AppKit loop driven from Node |
| Linux (Wayland/X11) | ✅ | ✅ physical input; synthetic wheel via automation | blocking UI thread |
| Windows | ✅ builds | same blocking-thread model |
Solo originated as a fork of GPUIX and has since diverged into an independent Solid-first native TypeScript desktop framework built on GPUI.
Inherited license notices are preserved in THIRD_PARTY_NOTICES.md and were not replaced by the transfer to independent ownership.