Server-driven framework for building stateful, reactive web applications entirely in Go.
翻译准备中
正在加载完整 README。
查看完整 README ↗Doors is a server-driven UI framework + runtime for building stateful, reactive web applications in Go.
Your Go server is a UI runtime: the web application runs on a stateful server, while the browser acts as a remote renderer and input layer. No endpoints to design, no JSON contracts, no client bundle — go build produces the web application as one binary.
Templates are GoX — a typed Go superset with its own parser, LSP, and editor plugins.
Live search:
type Search struct {
db *DB // your data layer
query doors.Source[string] // reactive state
}
elem (s *Search) Main() {
<input
(doors.AInput{
Scope: &doors.ScopeDebounce{Duration: 300 * time.Millisecond}, // debounce requests
On: func(ctx context.Context, r doors.RequestInput) bool {
s.query.Update(ctx, r.Event().Value) // update state
return false
},
})
type="search"
placeholder="search">
~(s.query.Bind(s.results)) ~// re-renders on query changes
}
elem (s *Search) results(query string) {
~~
users := s.db.SearchUsers(ctx, query) // hit the DB mid-render
~~
<ul>
~(for _, u := range users {
<li>~(u.Name)</li>
})
</ul>
}
This is the whole feature — no endpoint, no JSON contract, no fetch call, no client-side state, no JS build step. Debounce is one attribute, and the permission check happens where you render.
ctx.Done() when their subtree leaves the page. External systems integrate directly into the stateful session process — no wrapping stateful work into stateless logic.<-chan error.HTMX and Datastar enhance HTML by coordinating behavior through attributes and endpoints; templ adds typed HTML rendering in Go on top. Interaction still flows through endpoints you design, wire up, and authorize. Doors is a UI runtime: you write the interaction flow directly in Go, handlers attach to elements, state lives on the live page instance, and the endpoint layer disappears.
The same family: the server owns UI state. Doors does it in pure Go — no BEAM or .NET runtime, typed URL structs instead of string routes, plain-HTTP sync instead of WebSockets, one static binary. And the main thing: Doors UI is non-blocking. LiveView and Blazor serialize interactions through one process per client; Doors handles events concurrently, so interactivity behaves like a JavaScript SPA.
Livewire and Hotwire are stateless: every interaction is a plain HTTP request you must authorize. Doors runs the UI as a stateful process — multi-stage flows and server-triggered updates are natural, and there is no public action surface to authorize: each handler exists only for the user it was rendered to.
Typical JavaScript stacks place the interaction model in the browser and use the server as a data service — every feature needs an API surface, client state, and synchronization between the two. Doors keeps the flow on the server in Go, with the browser focused on display and input — as if your API had exactly one consumer: the framework.
For new applications, use doors-dev/doors-starter, the recommended ready-to-run Doors project skeleton.
The Get Started guide walks through a minimal hello-world app by hand, useful for understanding the basic pieces.
For AI-assisted development, install the Doors Skill for Claude Code, Codex, or OpenCode.
The trade: server memory per open page (~300 KB for a heavy production page) and interaction latency bounded by the network round trip.
Doors is used in production. Breaking changes are still happening, but they make life easier and are straightforward to migrate — each is documented in RELEASE_NOTES.md.
Doors is licensed under the Apache License 2.0.