# GPUI-CE: a community fork of Zed's GPUI, and what a fork costs

> GPUI-CE is a fork of the GPUI framework that Zed builds its editor on, kept API compatible with upstream for now and drifting on purpose afterwards. It ships 24 crates, a justfile with per-platform recipes, and a clear answer about what a fork is for.

**gpui-ce/gpui-ce** — GPUI – Community Edition

- Repository: https://github.com/gpui-ce/gpui-ce
- Website: https://gpui-ce.github.io/
- Stars: 1,402 · Forks: 154
- Language: Rust
- License: Apache-2.0
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/gpui-ce-gpui-ce

## A fork that promises compatibility, with a warning attached

GPUI-CE is a fork of Zed's GPUI UI framework, and the README is unusually direct about what that means. It is mostly API compatible with upstream, but that is changing.

That single sentence is the whole project in miniature. A fork exists because the fork's users want something upstream will not do, and the two usual shapes are a fork that stays close and pulls fixes upstream, and a fork that diverges. This one starts close, and the README says it is moving away, which is the more honest thing to say in advance than to discover two years later.

The stated long-term goal is to become the go-to Rust GUI library for applications of any size, with reusable components, native platform integration and control over performance, built on Zed's work while continuing to bring in upstream fixes. The comparison with other forks in the ecosystem is also stated: other forks often develop around the applications that use them, while GPUI-CE aims to support a broad range of applications with a focus on stability.

The project is Apache-2.0 licensed, with the licence text in `LICENSE.md`, and the workspace metadata describes it as a community fork of Zed's GPU-accelerated UI framework, authored by Nathan Sobo. The default branch is `main`, the last push was on 2026-10-17, and there are no GitHub releases, but the workspace is marked `publish = true`, so the crates are distributed through the registry rather than through release archives.

One governance point belongs in the FAQ of the README, and it is unusual enough to quote. The project follows the Rust Foundation's internal AI usage policy, and describes itself as a framework made by humans, valuing community participation and taking time to understand contributors' intentions and offer guidance.

## The styling API reads like a web framework

The first thing the README shows is the styling model, and it is the part that makes GPUI feel unlike a desktop toolkit.

```rust
div()
    .id("some_id_123")
    .flex()
    .items_center()
    .gap_2()
    .rounded_lg()
    .rounded_smoothing(0.8)
    .bg(rgba(0xffffff30))
    .backdrop_blur(px(12.0))
    .transitions(|transitions| transitions.bg(millis(200)))
    .hover(|style| style.bg(rgba(0xffffff60)))
    .child("Hello, GPUI")
```

There is no stylesheet, no class name and no layout file. `div()` returns an element, and every method on it returns another element, so a view is built by chaining. The method names are the Tailwind vocabulary, `flex`, `items_center`, `gap_2`, `rounded_lg`, and the values are typed Rust values, an `rgba` colour, a `px` length, a `millis` duration.

Two of those methods are worth pausing on because they have no equivalent in a typical desktop API. `rounded_smoothing(0.8)` is a corner treatment that goes beyond a radius, and `transitions` takes a closure that receives a transitions object, so the animation target is part of the type rather than a string somewhere. `backdrop_blur` is a GPU-accelerated effect, which says something about what this framework is optimising for.

The `hover` method takes a closure over the style, which is the pattern for pseudo-classes: the style is a function of state, not a fixed value.

The repository ships the examples the README links to, in `crates/gpui/examples/learn/`, among them `layout.rs` and `styling.rs`. That directory is the fastest way to see the whole surface, since the examples are small and each one is named after the feature it demonstrates.

## Entity, notify and the listener pattern

State in GPUI is an `Entity<T>`, and the interaction between state and view is what the second example shows.

```rust
div()
    .id("counter")
    .child(format!("Count: {}", self.count))
    .on_click(cx.listener(|this, _event, _window, cx| {
        this.count += 1;
        cx.notify();
    }))
```

`Entity<T>` is the handle used to access view and shared application state, and the example of a view with a `count` field shows the pattern. A click handler receives the entity, mutates the state, and calls `cx.notify()` to notify observers and update the view.

The `cx.listener` wrapper is the part that explains the design. The closure is handed `this`, which is the entity, so the handler mutates the state through the handle rather than through a captured reference. That is what makes the view a function of the entity's state, and it is why `cx.notify()` is necessary and explicit: nothing observes the change automatically, the entity has to be told.

It is a different model from a framework with a virtual DOM that diffs against a previous render, and the difference shows up in the code. Here the invalidation is a call, and the call is the point where a reader can see that the view is now out of date. The trade is that there is no diffing to pay for, and the cost is that a missed `notify()` is a stale view with no error.

The example lives in `crates/gpui/examples/learn/interactive_elements.rs`.

## Typed actions, key contexts and focus handles

Keyboard handling in GPUI is not a key press callback, it is an action system, and the third example is the whole of it.

```rust
use gpui::{App, KeyBinding, actions};

actions!(keybinds_example, [Increment, Reset]);

fn register_keybinds(cx: &mut App) {
    cx.bind_keys([
        KeyBinding::new("space", Increment, Some("Counter")),
        KeyBinding::new("backspace", Reset, Some("Counter")),
    ]);
}
```

The `actions!` macro declares typed actions, and `bind_keys` is called during app setup to bind Space and Backspace in the focused counter. The third argument of `KeyBinding::new` is the key context, which is how a binding is scoped: the same physical key can mean different things in different parts of the interface, and the context names the part.

The view then connects its focus handle and its action handlers:

```rust
div()
    .key_context("Counter")
    .track_focus(&self.focus_handle)
    .on_action(cx.listener(Self::increment))
    .on_action(cx.listener(Self::reset))
    .child(format!("Count: {}", self.count))
```

The two halves have to agree. The view declares the key context with `key_context`, tracks focus with `track_focus`, and handles actions with `on_action`, and the binding registered in `bind_keys` names that same context. A mismatch is silent, which is the main thing to be careful about when moving between a binding and the view that handles it.

The advantage over raw key handlers is that an action is not tied to a key. A menu item, a button and a keyboard shortcut can all dispatch the same action, and the action is a typed value rather than a string that is matched at runtime. The example is in `crates/gpui/examples/learn/actions_and_keybinds.rs`.

## uniform_list and canvas, the two performance paths

Two features in the README are really about performance, and they solve different problems.

`uniform_list` is the virtualized list. It takes a list id, an item count and a closure that renders a range:

```rust
uniform_list("items", 10_000, |range, _window, _cx| {
    range
        .map(|index| {
            div()
                .h(px(24.0))
                .child(format!("Item {index}"))
        })
        .collect()
})
.h(px(300.0))
```

The contract is that GPUI requests the item ranges needed for the visible area as you scroll, so a list of ten thousand rows only builds the rows that are on screen. The uniformity is the precondition, which is why there is a separate mechanism for rows of varying height, and why the example is explicit about the fixed 24 pixel row height.

`canvas` is the escape hatch for anything the element model cannot express. It takes two closures, one for layout and one for painting, and the painting closure gets the window so it can paint directly:

```rust
canvas(
    |_bounds, _window, _cx| {},
    |bounds, _state, window, _cx| {
        window.paint_quad(PaintQuad::new(bounds).background(rgb(0x5078f0)));
    },
)
.size(px(80.0))
```

The README also says that implementing `Element` is the route when you need control over layout and rendering, with a code editor or a custom widget given as the examples. So there are three levels of control: the element and style methods for the common case, `canvas` for custom painting, and the `Element` trait for widgets that have to own their own layout.

The examples are `crates/gpui/examples/learn/uniform_list.rs` and `custom_drawing.rs`.

## Twenty-four crates, one default member, and a justfile

The workspace is where the platform story shows.

The `Cargo.toml` lists 25 members, and the names describe the structure: `gpui` and `gpui_render` at the centre, `gpui_web` and `gpui_wgpu` for the rendering backends, `gpui_linux`, `gpui_macos`, `gpui_apple` and `gpui_windows` for the platform layers, `gpui_platform` and `gpui_shared_string` for shared abstractions, `gpui_elements` and `gpui_macros` for the element library, and a long tail of utility crates, `gpui_ce_fonts`, `gpui_collections`, `gpui_refineable`, `gpui_scheduler`, `gpui_sum_tree`, `gpui_media`, `gpui_zed_util`, `gpui_ce_util` and `gpui_path`, plus `tooling/perf`. The only default member is `./crates/gpui/`, so a plain `cargo build` in the workspace root builds the core and nothing else.

The dependency list is where the text and accessibility work lives: `accesskit` with the `enumn` feature and its per-platform consumers, `parley` for text layout, `fontique` for font handling, `swash` for font shaping, `unicode-bidi` with hardcoded data for bidirectional text, `heapless` for fixed capacity collections, `etagere` for a work allocator, `pathfinder_geometry` for path geometry and `raw-window-handle` for the platform window handle. Text layout, fonts and accessibility are not incidental in a text editor, and a framework that treats them as first-party crates is making a statement about what it is for.

The `justfile` holds the build recipes, and it is a good map of the project's structure. There is `check` for compilation errors across the workspace, `build` and `build-release`, one recipe per platform such as `cargo build -p gpui_ce_linux`, `cargo build -p gpui_ce_macos` and `cargo build -p gpui_ce_windows`, `build-examples` for the examples, and a wasm check for the `wasm32-unknown-unknown` target. The recipes run their commands through nushell, since the file sets the shell to `nu -c`.

The tooling is present as well: `audit.toml` and `deny.toml` for dependency auditing and licence checking, `clippy.toml` and `typos.toml` for lint configuration, a `flake.nix` for a Nix development environment, and a `CONTRIBUTING.md`.

The minimum supported Rust version is set in the justfile as 1.95.0, which is a very recent toolchain requirement and worth noting before anyone tries to build this on a pinned distribution toolchain.

## Where GPUI-CE is the wrong tool

The case for a fork is a case about control, and the costs are the same shape.

The first cost is drift, and the project says so itself: the API is mostly compatible with upstream, but that is changing. A team that adopts GPUI-CE and then wants a fix that lands upstream has to port it, because the fork's own direction may not match. A team that adopts upstream GPUI instead has the opposite problem, which is that it has no say in the direction and must take what upstream does. Neither is free, and the difference is who carries the merge work.

The second cost is the toolchain. Rust 1.95.0 as the minimum, edition 2024, and a workspace of 24 crates with per-platform builds, is a heavy build. The justfile has recipes for Linux, macOS and Windows separately, and a wasm check, which is a sign of a project that is compiled on several platforms rather than one. A team on a pinned corporate toolchain will find the requirement awkward.

The third is that the pieces GPUI-CE inherits are not small. Zed's GPUI carries its assumptions about what an application is, and a fork of a framework built for a code editor is a framework built for a code editor. The 24 crates include text layout, fonts, shaping, accessibility and a media stack, which is a large surface with a large amount of code that has to be kept compiling against upstream changes.

The alternative is upstream GPUI itself, used under Zed's own terms, with the difference in approach that upstream gets the fixes first and the fork gets to choose its own direction. For an application that simply wants a fast, GPU-accelerated Rust UI, upstream is the smaller commitment, and the only reason to take the fork is a reason specific enough to justify carrying the merge work: for instance needing a change upstream will not take, or needing the crates published under a name and licence that suit the organisation.

The project's own answer to that question is the stability focus, and it is a reasonable one for a fork that wants to be a general purpose library rather than an application-specific one.

## Conclusion

GPUI-CE fits a team that wants Zed's rendering model and its web-inspired styling without adopting the editor, that wants to pull in upstream fixes on their own schedule, and that can live with an API that is explicitly still changing. It is the wrong choice for a project that needs a stable public API, because the fork states that its compatibility with upstream is changing, and it is the wrong choice for a team that cannot follow a moving toolchain, since the workspace asks for Rust 1.95 or newer. Before depending on it, read `SETUP.md` and the examples under `crates/gpui/examples/learn`, decide whether you want the fork or upstream GPUI, and note that the project publishes a human-made policy that shapes how contributions are reviewed.

## FAQ

### What is GPUI-CE?

It is a community fork of Zed's GPU-accelerated UI framework, published under Apache-2.0 and built as a Rust workspace of 24 crates. The README says it is mostly API compatible with upstream, but that this is changing.

### How do I style elements in GPUI-CE?

With a web-inspired API. `div()` returns an element you chain methods on, such as flex, items_center, gap_2, rounded_lg, bg with an rgba colour, backdrop_blur, transitions and hover, finishing with child. The layout and styling examples are in crates/gpui/examples/learn.

### How does GPUI-CE handle a list with many rows?

With `uniform_list`, which takes a list id, an item count and a closure that renders a range. GPUI requests the item ranges needed for the visible area as you scroll, so only the visible rows are built.

### How do I build GPUI-CE from source?

The justfile holds the recipes, including check, build, build-release, per-platform builds such as `cargo build -p gpui_ce_linux`, `cargo build -p gpui_ce_macos` and `cargo build -p gpui_ce_windows`, plus build-examples and a wasm check against the wasm32-unknown-unknown target. The setup guide is SETUP.md.

## Sources

- [gpui-ce/gpui-ce on GitHub](https://github.com/gpui-ce/gpui-ce)
- [Issues](https://github.com/gpui-ce/gpui-ce/issues)
- [License: Apache-2.0](https://github.com/gpui-ce/gpui-ce/blob/main/LICENSE)
- [Project website](https://gpui-ce.github.io/)
- [README](https://github.com/gpui-ce/gpui-ce/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/gpui-ce-gpui-ce
