Get Started
Install GoX
Ensure your Go is at least 1.25.1; you can check in the terminal by running
go version
Doors is built on top of GoX, a purpose-built Go language extension that turns HTML templates into typed Go expressions and adds elem primitives.
GoX comes with its own language server, which mostly acts as a gopls proxy while adding extra features on top.
Please use the official VS Code or Neovim extension. Alternatively, follow the manual installation guide in the GoX README.
It is also recommended to have the gox binary on your PATH.
That lets you run commands such as gox fmt and gox gen yourself, and it also helps editor tooling and code agents trigger the same workflow when needed.
GoX Workflow
The practical workflow is simple:
- write GoX source in
.gox - use
.gofor ordinary Go files when that fits naturally - treat
.x.goas generated output, don't edit it
The language server keeps generated files up to date while you work, and you can use
gox genorgox fmtwhen you want to run generation or formatting yourself.
Setup Project
Create a new directory containing our project:
mkdir hello-doors
Initialize a new Go module there and get Doors:
cd hello-doors
go mod init github.com/doors-dev/doors-examples/hello-doors
go get github.com/doors-dev/doors
App Component
Write a component with the page template to app.gox:
package main
import "github.com/doors-dev/gox"
type App struct{}
elem (a App) Main() {
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Hello Doors!</title>
</head>
<body>
<main class="container">
<h1>Hello Doors!</h1>
</main>
</body>
</html>
}
Components in Doors (and GoX) must have a Main() method that returns gox.Elem. The elem keyword lets you write an HTML template directly in the function body.
The GoX language server generates and manages
.x.gofiles automatically.
Serve The App
Create the app, hand it to Go's HTTP server, and run it. Put this in main.go:
package main
import (
"context"
"net/http"
"github.com/doors-dev/doors"
"github.com/doors-dev/gox"
)
func main() {
app := doors.NewApp(func(ctx context.Context, r doors.Request) gox.Comp {
return App{}
})
if err := http.ListenAndServe(":8080", app); err != nil {
panic(err)
}
}
Start the program with go run . and open http://localhost:8080.
Safari on localhost
Doors uses a
Secureinternal session cookie by default. Chrome and Firefox usually accept secure cookies onhttp://localhost, but Safari rejects them on plain HTTP. If you test with Safari locally, either use local HTTPS or disable theSecureattribute for development:app := doors.NewApp(page, doors.WithConf(doors.Conf{ ServerSessionCookieNoSecure: true, }))
What Just Happened?
doors.NewApp(...) builds the Doors application. It takes a single page function that, for every request, returns the root component Doors should render:
app := doors.NewApp(func(ctx context.Context, r doors.Request) gox.Comp {
return App{}
})
doors.Requestexposes request and response headers, cookies, and the underlying HTTP context. Thectxis the Doors runtime context for the new page instance — use it for Doors APIs.
The returned app is an http.Handler, so it plugs straight into the standard Go server:
http.ListenAndServe(":8080", app)
Configuration options (CSP, request timeouts, error pages, etc.) and middleware for static files live on the same app value. See App and Configuration.
Dynamic Update
At this point you have a static page.
To see the basic Doors flow, turn it into a tiny interactive counter.
Update the imports in app.gox and add a small counter component:
import (
"context"
"github.com/doors-dev/doors"
"github.com/doors-dev/gox"
)
type Counter struct {
count int
door doors.Door // dynamic container
}
elem (c *Counter) Main() {
<button
(doors.AClick{
On: func(ctx context.Context, _ doors.RequestPointer) bool {
c.count += 1
c.door.Inner(ctx, c.count)
return false
},
})>
Click Me
</button>
~>(c.door) <span>0</span>
}
And render it on the page:
elem (a App) Main() {
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Hello Doors!</title>
</head>
<body>
<main class="container">
<h1>Hello Doors!</h1>
~(&Counter{})
</main>
</body>
</html>
}
Now the browser click is sent back to Go, the handler updates the counter state, and the dynamic <span> shows the current value.
Next
- Core Concepts explains the runtime model behind sessions, instances, doors, hooks, and state.
- Template Syntax covers the GoX syntax used throughout the docs.
- App and Routing take the next step into request handling and URL design.
- State covers reactive state.