GPUIX: React and Solid bindings for Zed's GPUI, with no web view
Node.js & React bindings for Zed’s GPUI. Build memory efficient native apps with React and no Electron
At a glance
- What is it?
- GPUIX renders a React or Solid tree through GPUI onto Metal, DirectX or Vulkan, so a native window replaces the browser runtime. The hard part is not the API, it is keeping the adapter and the native binary on the same exact version.
- Who is it for?
- Adopt GPUIX if you already write React or Solid and want a desktop window without a browser runtime, and you accept pinning @gpuix/react and @gpuix/native to the same exact version on every upgrade. Do not adopt it for a web app that also needs to run in a browser, or for a team that cannot rebuild when a pre-1.0 release changes.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 28, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What GPUIX replaces, and who it is written for
Electron puts a Chromium build and a Node runtime inside every desktop app. GPUIX takes the opposite route. The README describes it as React and Solid for GPUI, the GPU UI framework used by Zed, and states that GPUIX paints the tree with Metal, DirectX or Vulkan. There is no web view in the process.
The audience is narrow and specific. You need to be comfortable in TypeScript and JSX, because the component model is still React or Solid. You also need to care about binary size or memory enough to give up the DOM. The README gives two reference numbers for packaged macOS apps: a Bun chat app at 82 MB and a Hermes counter app at 34 MB. Those are the author's own measurements of two example apps, not a general guarantee, but they set the order of magnitude you are choosing between.
If your UI is a form over a REST API and your users already have a browser, this project is the wrong shape. The value only appears when the window itself is the product.
How the React tree reaches the GPU
The mechanism is a custom React reconciler. The repository's root package.json lists react-reconciler as a dependency, and the README says layout, text and input go through GPUI rather than the DOM. So JSX elements are not turned into DOM nodes at any point. The reconciler produces a tree that the native renderer consumes, and GPUI lays out and paints it.
That explains several details that look odd next to ordinary React. The README notes that GPUI does not inherit color from a parent, which is why every <text> needs its own color or it paints black. It also documents a hover key inside a style object, which is not a CSS property but a GPUIX extension handled by the native layer.
The entry point is a single render() call that creates the window, mounts the tree and starts the frame loop. Editing app.tsx remounts React on the same window, and the README states that click and keyboard handlers switch to the new tree without recreating the window. That is the practical payoff of the reconciler approach: state lives in JS, the window survives reloads.
GPUIX also ships a Solid adapter as @gpuix/solid, so the same native renderer serves two component models.
Install GPUIX and run a first window
The documented path avoids a repository clone, a native build and a Rust toolchain entirely. The CLI downloads only example-app/ and installs its dependencies.
bunx @gpuix/cli new my-app
cd my-app
bun run devIf you would rather assemble the project yourself, the README gives this install line and one important warning: pin the adapter and the native package to the same exact version, because a version range can pull a newer native binary under an older adapter.
bun add --exact @gpuix/react @gpuix/native react
bun add -d @types/react typescriptTypeScript needs to be pointed at the GPUIX JSX types. Without jsxImportSource, the README says TypeScript falls back to DOM types and elements such as <virtual-list>, <markdown> and <code>, plus style.hover, fail to typecheck.
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "@gpuix/react",
"moduleResolution": "bundler",
"strict": true
}
}A minimal app ends with render(), which creates the window and starts the frame loop.
import { useState } from 'react'
import { render } from '@gpuix/react'
function App() {
const [count, setCount] = useState(0)
return (
<div style={{ padding: 24, height: '100%' }}>
<text style={{ color: '#e2e2e2' }}>Count: {count}</text>
</div>
)
}
render(<App />, { title: 'My App', width: 800, height: 600 })Run it with hot reload. The README is explicit that plain bun is not enough here: a save should remount React on the existing window rather than open a second one.
bun --hot app.tsxTo ship, compile the app into a single binary that carries the renderer and needs neither Bun nor Node on the target machine.
bun build --compile app.tsx --outfile dist/app
./dist/appVersion pinning is the sharpest edge in the project
GPUIX is pre-1.0 and the README says so directly: a new release can break either the adapter or the native package. The failure mode is not a compile error you can read. The adapter declares a version range for @gpuix/native, so a fresh install can resolve a newer native binary than the adapter was written against. Adding both as direct dependencies with --exact is the documented mitigation, and upgrading them together is the documented discipline.
The packaging path has its own traps. A raw Mach-O has no Dock icon, so the README points at cargo-packager to wrap the binary. It reports that passing a 1024 PNG was rejected with No matching IconType, which is why the instructions build a proper .icns through an iconset first. For the Hermes path the .node sidecar must be listed as a second binary rather than a resource, because dlopen looks in Contents/MacOS while resources land in Contents/Resources.
The updater is a separate decision from packaging. The README states plainly that packaging does not turn on updates. The app calls checkUpdate on @gpuix/native, which reads tag_name and assets from the GitHub Releases API and then fetches the sibling .sig file. If you do not publish signed releases, that path has nothing to consume.
GPUIX against Electron and Tauri
Electron keeps the web platform and pays for it with an embedded Chromium and Node in every install. The difference is not only size. In Electron your layout engine is Blink and your rendering path is the browser's; in GPUIX the README says layout, text and input go through GPUI. That means the CSS you know does not apply wholesale, and the component libraries built for the DOM will not drop in.
Tauri keeps the system web view instead of bundling one, which shrinks the installer but leaves you dependent on whatever engine the host OS provides, and still means your UI is HTML and CSS. GPUIX removes the web view from the equation and gives you a single native renderer chosen per platform: Metal, DirectX or Vulkan.
The cost of that trade is ecosystem. A React component that renders a div with a box-shadow has no GPUIX equivalent unless the native layer implements it. The README's own example leans on primitives like <virtual-list>, <markdown> and <code>, which tells you the useful surface is the set of elements the native renderer knows about, not the whole of HTML.
Licence, maintenance and the cost of upgrading
The repository is licensed Apache-2.0, and the root package.json carries the same identifier. Apache-2.0 includes an express patent grant and permits commercial and closed-source use, but it also carries notice and attribution obligations, and the repository ships a THIRD_PARTY_NOTICES.md file, which suggests bundled dependencies need their notices carried forward. That is a question for your own legal review, not something to settle from a README.
The last push to the repository was on 2026-09-27, and the repository is not archived. Recent releases include @gpuix/[email protected] and @gpuix/[email protected], both 0.x, and a [email protected] package. The 0.x version numbers are the honest signal here: the project states that releases can break the adapter or the native binary, so an upgrade is a test-and-rebuild task rather than a version bump.
Budget for that. Every adapter upgrade should be paired with the matching @gpuix/native release, a rebuild of the compiled binary, and a re-run of whatever tests you have. The repository keeps an examples/ directory with test files such as chat.test.tsx and timeline.test.tsx, which is a reasonable place to look for patterns, though the README does not promise those tests cover your platform.
Editorial conclusion
Adopt GPUIX if you already write React or Solid and want a desktop window without a browser runtime, and you accept pinning @gpuix/react and @gpuix/native to the same exact version on every upgrade. Do not adopt it for a web app that also needs to run in a browser, or for a team that cannot rebuild when a pre-1.0 release changes. Before committing, verify that a Bun-compiled binary launches on your target OS, that every <text> in your tree carries an explicit color, and that checkUpdate against your own GitHub Releases endpoint returns the assets you expect.
Frequently asked questions
Does GPUIX use a web view or Electron?
No. The README states that GPUIX paints the React or Solid tree with Metal, DirectX or Vulkan, and that layout, text and input go through GPUI rather than the DOM.
How do I install GPUIX?
The documented quickstart is bunx @gpuix/cli new my-app, then cd my-app and bun run dev. The CLI downloads only example-app/ and installs its dependencies, so no repository clone, native build or Rust toolchain is needed.
Why do my GPUIX text elements render black?
GPUI does not inherit color from a parent element. The README warns that text without its own color paints black and disappears on a dark surface, so every <text> needs an explicit color.
Can I ship a GPUIX app without Bun or Node installed?
The README gives bun build --compile app.tsx --outfile dist/app, and states the resulting binary carries the renderer, so it runs with no Bun and no Node install.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/remorses-gpuix)