GoFrame is an experimental Go-first framework and toolchain for interactive
browser/WASM apps. It combines a small Go runtime, JSX-like .gox markup, the
goxc generate/build/package workflow, and standard Go or TinyGo WebAssembly
output.
The current validated surface is browser/WASM applications. GoFrame is not
production-ready, does not claim stable 1.0 APIs, and does not ship
SSR/hydration, fullstack/server APIs, Player/Engine, .gfapp, formatter, or
LSP support.
Install goxc, add its install directory to PATH, check your local
toolchain, package the counter example, and serve it locally:
go install github.com/graybuton/goframe/cmd/goxc@latest
goxc_bin="$(go env GOBIN)"
if [ -z "$goxc_bin" ]; then
goxc_bin="$(go env GOPATH)/bin"
fi
export PATH="$goxc_bin:$PATH"
goxc doctor
goxc package ./examples/counter --compiler=tinygo
goxc serve ./examples/counter --port=8080Open http://127.0.0.1:8080.
TinyGo gives the smallest browser bundles when available. Standard Go remains useful for compatibility and local inspection:
goxc package ./examples/counter --compiler=gogoxc serve is a development server for packaged examples, not production
hosting infrastructure.
package main
func App(props AppProps) gf.Node {
count, setCount := gf.UseState(0)
return (
<main>
<h1>Counter</h1>
<p>Count: {count}</p>
<button onClick={func() { setCount(count + 1) }}>
Increment
</button>
</main>
)
}
goxc check validates authored .gox files without writing generated output.
goxc generate turns .gox files into Go compiler output under .goframe;
it does not write generated .gox.go files next to authored source by default.
pkg/goframe: browser/WASM runtime primitives for nodes, components, hooks, context, events, reconciliation, resources, routing, and fixed-height virtualization.pkg/gox: GOX lexer/parser/codegen with source-oriented diagnostics.cmd/goxc: check, generate, build, package, export, serve, dev, size, clean, doctor, and version commands.- Examples and scripts that exercise the runtime, compiler, package workflow, browser smoke paths, and WASM size budgets.
- A lightweight VS Code GOX extension in
extensions/vscode-gox.
- GoFrame Tutorial - guided router-dashboard walkthrough.
- Evaluator guide - short path for trying the current browser/WASM preview.
- GOX language - syntax, diagnostics, and limits.
- Runtime model - component identity, hooks, reconciliation, resources, and errors.
- Local development - watched development packaging, loopback serving, failure recovery, and current non-goals.
- Deployment - package layout, asset hashing, preloads, export, and cache-safe static delivery.
- API stability and platform support - maturity and support boundaries.
- Releases - published previews and release notes.
- Release process - versioning, validation, and publishing policy.
- Roadmap - status-qualified direction for the application model, modular delivery, SSR/hydration research, tooling, and fullstack contracts beyond the current shipped surface.
For the fastest tour, start with examples/counter, then
examples/components, then examples/router-dashboard.
Examples
| Example | What it demonstrates | Command |
|---|---|---|
examples/counter |
Minimal state, events, TinyGo quickstart, package/serve workflow. | goxc package ./examples/counter --compiler=tinygo |
examples/components |
Typed props, children, fragments, component composition. | goxc package ./examples/components --compiler=tinygo |
| Example | What it demonstrates | Command |
|---|---|---|
examples/todo |
Controlled inputs, events, effects, keys, list helpers, localStorage. | goxc package ./examples/todo --compiler=tinygo |
examples/context |
Scoped providers, selector consumers, broad UseContext, nested providers. |
goxc package ./examples/context --compiler=tinygo |
examples/virtualized |
gf.VirtualList, gf.VirtualTable, bounded DOM with 10,000 logical rows. |
goxc package ./examples/virtualized --compiler=tinygo |
examples/router |
Hash router, query helpers, route params, not-found route, and stable shell layout. | goxc package ./examples/router --compiler=tinygo |
examples/resource |
gf.UseResource loading, ready, failed, stale completion, reload, and cleanup behavior. |
goxc package ./examples/resource --compiler=tinygo |
| Example | What it demonstrates | Command |
|---|---|---|
examples/dashboard |
Reducer dispatch, explicit memoization, virtual table, dashboard pressure smoke. | goxc package ./examples/dashboard --compiler=tinygo |
examples/router-dashboard |
Reference app: hash router, query-state filters, component-scoped resource data, forms, validation, Error Boundary, and Go-first layout. | goxc package ./examples/router-dashboard --compiler=tinygo |
examples/server-backed |
Browser/WASM app served by plain Go net/http with a same-origin API. |
goxc package ./examples/server-backed --compiler=go |
| Example | What it demonstrates | Command |
|---|---|---|
examples/multipackage |
entry: "." app with root plus internal/... packages. |
goxc package ./examples/multipackage --compiler=tinygo |
examples/cmdapp |
Child entry package with "entry": "./cmd/app" and Go-first layout. |
goxc package ./examples/cmdapp --compiler=tinygo |
Serve any packaged example with:
goxc serve ./examples/<name> --port=8080Create cache-safe deployment-style artifacts with:
goxc package ./examples/<name> --compiler=tinygo --asset-hash --preload --compress=gzip,brUse goxc export when you want a visible deploy directory:
goxc export ./examples/counter --out ./distProject Layers
| Layer | Role |
|---|---|
| GOX language | JSX-like declarative markup embedded in Go source. |
pkg/goframe runtime |
Nodes, component instances, hooks, context, events, reconciliation, resources, routing, and browser mounting. |
pkg/gox compiler |
GOX lexer/parser/codegen plus diagnostics and golden tests. |
cmd/goxc toolchain |
Generate, build, package, export, serve, size, clean, doctor, and version commands. |
| examples and scripts | Integration probes, browser smoke, size budgets, and package/artifact gates. |
| VS Code extension | Syntax highlighting, snippets, file icons, and commands over the same goxc workflow. |
Single-package apps can use entry: ".":
app/
├── goframe.json
├── app.gox
├── main.go
└── assets/
├── index.html
└── styles.css
Larger apps can use a Go-style child executable entry package:
app/
├── goframe.json
├── assets/
│ ├── index.html
│ └── styles.css
├── cmd/app/
│ ├── main.go
│ └── app.gox
└── internal/
└── ui/
└── layout.gox
{
"entry": "./cmd/app",
"compiler": "tinygo",
"assets": "./assets"
}GOX discovery is app-root-wide, so imported internal packages get generated
files too. Cross-package component composition uses normal Go imports with
package-qualified tags such as <ui.Header />.
GOX In One Minute
Lowercase tags create DOM elements:
<button onClick={increment}>Increment</button>
Capitalized tags create runtime component boundaries using
<ComponentName>Props:
<Button Label="Increment" OnClick={increment} />
type ButtonProps struct {
Label string
OnClick func()
}Package-qualified component tags use ordinary Go import aliases:
import ui "github.com/example/app/internal/ui"
func App() gf.Node {
return (
<ui.Shell Title="Dashboard">
<p>Hello</p>
</ui.Shell>
)
}
The supported form is exactly packageAlias.Component. It is not an XML
namespace system and does not support arbitrary selector chains.
Children use Children []gf.Node. Fragments use <>...</>.
Key={...} is a pseudo-prop for reconciliation and is not passed to component
props or emitted as a DOM attribute.
GOX supports expression-oriented rendering:
{ready && <ReadyView />}
{len(items) == 0 ? (
<EmptyState />
) : (
<ItemList Items={items} />
)}
{gf.Map(items, func(item Item) gf.Node {
return <ItemRow Key={item.ID} Item={item} />
})}
Unsupported syntax is intentionally rejected with source diagnostics, including
XML-style namespace tags, arbitrary selector chains, spread props,
template-block if/for, and arbitrary JavaScript-like expressions.
See GOX language.
Runtime Primitives
State is component-scoped and positional:
count, setCount := gf.UseState(0)
setCount(count + 1)Reducer dispatch applies an action to the latest state slot at event time, which avoids stale render captures in retained event handlers:
type CounterAction int
count, dispatch := gf.UseReducer(0, func(state int, action CounterAction) int {
return state + int(action)
})
dispatch(1)Effects run after DOM patching:
gf.UseEffect(func() gf.Cleanup { return nil })
gf.UseEffect(func() gf.Cleanup { return nil }, gf.Deps(value))
gf.UseUnmount(func() {})Context is scoped by the component tree:
var PreferencesContext = gf.CreateContext(Preferences{})
gf.ProvideContext(PreferencesContext, preferences)
density := gf.UseContextSelector(PreferencesContext, func(value Preferences) string {
return value.Density
})Memoization is explicit and opt-in: implement MemoEqual on props when a
component can safely skip render/patch for equivalent props. Large fixed-height
collections should use gf.VirtualList or gf.VirtualTable instead of
mounting hidden offscreen rows.
The small client router is hash-based:
var router = gf.NewHashRouter([]gf.Route{
gf.RoutePath("/", homeRoute),
gf.RoutePath("/issues/:id", issueRoute),
gf.NotFoundRoute(notFoundRoute),
})
func App() gf.Node {
return gf.RouterView(router)
}Forms use ordinary runtime primitives: controlled inputs, gf.InputEvent,
gf.Event.PreventDefault, and application-owned validation state. GoFrame does
not include a schema validation framework.
Render failures can be contained with an experimental scoped Error Boundary:
<gf.ErrorBoundary ResetKey={routeKey} Fallback={fallback}>
<pages.Content />
</gf.ErrorBoundary>
Boundaries catch descendant render failures, report through
gf.SetErrorHandler, and render fallback UI until reset. They do not catch
event, effect, cleanup, memo comparator, or context update failures.
Toolchain Workflow
Generated, build, and package outputs live under an app-local hidden workspace:
<app>/.goframe/
├── gen/
├── work/
├── build/
└── package/
Use GOFRAME_WORKSPACE=/work/goframe or --workspace /work/goframe when the
source tree is read-only.
Common commands:
| Command | Responsibility |
|---|---|
goxc check <file-or-directory> |
Validate authored .gox source without writing generated files. |
goxc generate <app> |
Generate .gox.go compiler output under .goframe/gen. |
goxc build <app> |
Compile raw WASM under .goframe/build/<compiler>/dev. |
goxc package <app> |
Build a runnable .goframe/package/standalone bundle. |
goxc export <app> --out <dir> |
Copy the latest package to an explicit deploy directory. |
goxc size <app> |
Report size from .goframe/package/standalone. |
goxc serve <app> |
Serve the local package for development. |
goxc dev <app> |
Package, serve, and rebuild effective authored inputs for local development. |
goxc clean <app> |
Remove tool-owned workspace/build/package artifacts. |
goxc doctor |
Check local Go/TinyGo/compression/runtime-shim tools. |
goxc package normalizes output to:
<app>/.goframe/package/standalone/
├── index.html
├── asset-manifest.json
├── goframe-package.json
└── assets/
├── bundle.wasm
└── wasm_exec.js
Use "assets": "./assets" for app static files. If assets/index.html exists,
it is rewritten as the package root HTML entrypoint; otherwise goxc package
generates a default index.html.
Current Limitations
- Experimental browser/WASM target only.
- Hash router only; no SSR, hydration, path/history-mode server fallback, file-based routing, route loaders, hot reload, CSS-in-Go, or production deployment server.
- Player/Engine and
.gfappare inactive directions outside the current preview contract. - No XML-style namespace tags, arbitrary selector chains, spread props, template-block loops, formatter, or LSP.
- No full multi-module monorepo story.
- State/effect/context hooks are positional and require stable call order.
- Context selectors require comparable selected values.
- Memoization is explicit; there is no automatic deep equality or function-prop equality.
- Virtualized collections are fixed-height only; no dynamic measurement, infinite loading, or advanced keyboard/accessibility layer yet.
- Error Boundaries are render-only and recover-based. Default TinyGo
panic=trapbuilds compile the API but are not the proof path for boundary containment. - Resources are component-scoped and explicit-state only.
gf.FetchTextis a low-level experimental browser text loader; there is no global resource cache, Suspense-style render blocking, route loader framework, JSON/data framework, or higher-level fetch API. - Duplicate key diagnostics are debug-only.
- TinyGo compatibility remains version- and feature-dependent.
Development Checks
Core local checks:
go fmt ./...
go test ./...
go test -race ./pkg/... ./cmd/...
go vet ./...
go test -tags=goframe_debug ./...
go test ./pkg/gox -run 'TestGolden|TestErrorGolden'Full local gate:
scripts/check.sh
scripts/size-budget.sh
scripts/perf-report.sh
scripts/browser-smoke.sh
node --experimental-websocket scripts/dashboard-dom-pressure.mjs
scripts/artifact-check.sh
scripts/module-path-check.shCI runs core Go/GOX checks, docs consistency, TinyGo WASM size budgets, browser smoke, dashboard DOM pressure, artifact/module gates, and VS Code extension compile checks.
VS Code Support
The MVP .gox extension lives in extensions/vscode-gox. It provides syntax
highlighting, snippets, file icons, and command-palette actions that shell out
to goxc.
Local extension development:
cd extensions/vscode-gox
npm install
npm run compile
code .Full Documentation Map
Start here:
- GoFrame Tutorial
- Evaluator guide
- Published releases and release notes
- Release process
- Architecture and toolchain boundaries
- Runtime model
- GOX language and diagnostics
- API stability tiers
- Compatibility and deprecation policy
- Migration note template
- Platform support matrix
- CI and regression gates
Runtime topics:
- Lifecycle and effects
- Runtime error semantics
- Error Boundaries
- Forms and validation patterns
- Explicit memoization
- Context selectors
- Virtualized collections
- Client router
- Component-scoped resources
- Component identity strategy
- Component identity next steps
Toolchain and delivery:
- Multi-package GOX workspace
- Local development workflow
- Cache-safe package delivery
- Manifest compatibility
- Symlink and file safety policy
- Performance baseline
- Release hygiene
Examples:
- Counter example
- Components example
- Todo example
- Dashboard pressure-test example
- Context selectors example
- Virtualized collections example
- Multi-package GOX example
- Child entry package example
- Router example
- Router dashboard reference example
- Resource loading example
- Server-backed reference example
- VS Code GOX extension
goframe is licensed under the Apache License, Version 2.0.