goui

module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 17, 2026 License: MIT

README

GoUI is a Go server-driven UI framework. You write components in Go; the server owns state, renders HTML, and pushes minimal DOM patches over WebSocket. The browser runs a small vanilla JS runtime—no React/Vue bundle, no client-side component tree.

Inspired by the LiveView idea (server-authoritative views over a persistent connection), GoUI is an independent implementation with a framework-agnostic core, HTTP adapters (net/http, Chi, Fiber, Gin, Echo), a keyed HTML diff engine, and a progressive forms library (native Tier 1 + rich Tier 2 controls).

Use GoUI when you want one language (Go) for domain logic and UI, keep form state on the server (no Laravel-style old() gymnastics after validation failure), and ship interactive pages without a SPA toolchain.

Why GoUI?

Approach What you get Cost
React/Vue SPA Rich client UX, huge ecosystem Duplicate models, API surface, build pipeline
Classic MPA Simple HTML Full page reloads, awkward form round-trips
HTMX Progressive enhancement Still mostly request/response; complex state is DIY
GoUI Component model in Go, live patches, shared validation/i18n Persistent WS per session

Prefer GoUI when: internal tools, admin/ERP panels, multi-step forms, tenant apps where Go already owns the domain.

Prefer something else when: offline-first mobile apps; millions of concurrent cheap page views where long-lived WebSockets are too expensive; teams that need a large client-component marketplace; pure marketing sites with almost no interactivity.

Architecture

Browser (goui.js)
    │  event / prefetch / activate
    ▼
Session ──► Component.HandleEvent / Mount
    │
    ▼
Render HTML ──► Diff (old tree → patches) ──► Frame(render)
    │
    ▼
Hub (sessions, grace reconnect, Broadcast)
  1. Client connects to /goui/ws?component=…
  2. Server creates a Session, mounts the component, sends session + initial render (OpReplace)
  3. User events become event frames → HandleEvent → re-render → minimal patches
  4. Optional: prefetch mounts silently; activate promotes and renders
  5. Disconnects keep the session for a grace period (default 60s) so reconnect restores state

Features (Phases 1–9)

Core

  • Component lifecycle: Mount / Render / HandleEvent / Unmount
  • BaseComponent: dirty tracking, i18n helpers, toast helpers
  • Registry factories, HTML template cache (RenderTemplate)

i18n

  • JSON locales, nested-flat keys (form.required_field)
  • Fallback to base locale tr, then [[key]] placeholder

WebSocket / Session / Hub

  • Framework-agnostic ws.Server + nested HTTP adapters
  • Reconnect with session id; grace period cleanup
  • Frames: event, render, push, error, session, prefetch, activate

Diff engine

  • HTML parse → tree → patches (replace, update_text, set_attr, remove_attr, insert, remove, move)
  • Keyed list diff via data-key (simple key-map, not LCS)

Client runtime

  • Vanilla JS: patch apply, event delegation (g-click, g-change, g-input, g-submit)
  • Modules: toast, prefetch, selectable, calendar, otp, richtext, codeeditor, upload, avatar, signature

Forms Tier 1 (native)

TextInput, NumericInput, DateTimeInput, ChoiceInput (checkbox/radio), FileInput, ColorInput, HiddenInput, Textarea, Select/Option/Optgroup, Button, Form/Fieldset/Legend/Label, Datalist, Output, Meter, Progress

Validation

Required, MinLength, MaxLength, Pattern, Email, NumericRange, Custom — server-side; state stays in the component after failed validation

Forms Tier 2

  • A: Searchable Select, Multi Select, Combobox, Autocomplete, Tag/Chips, Tree Select, Cascader, Dual Listbox
  • C: Currency, Percentage, Rating
  • B: Date Range, Time Range, Calendar Picker
  • F: OTP/PIN, Phone, Country/Language/Timezone/Currency pickers
  • E: Rich Text (Quill), Markdown (goldmark), Code Editor (CodeMirror)
  • D: Drag&Drop / Image / Avatar upload + cropper overlay
  • G: Emoji/Icon/Font pickers, Color swatch / Gradient, Signature, Mention, Character counter, Password strength

Push / Toast

Toast / ToastT, Hub.Broadcast, kinds: success / error / warning / info

Prefetch

data-goui-prefetch + data-goui-activate; silent Mount; LRU cap 5; no pre-render

Requirements

  • Go 1.25.0 (see go.mod)
  • One HTTP adapter: adapters/stdlib (net/http / Chi), adapters/fiber, adapters/gin, or adapters/echo
  • Browser: WebSocket; IntersectionObserver recommended for prefetch on mobile
  • Tailwind CLI (optional) — only if you want utility CSS beyond forms/style.css

Install

go get github.com/zatrano/goui@latest
# pick an adapter, e.g.:
go get github.com/zatrano/goui/adapters/stdlib@latest
# or: adapters/fiber | adapters/gin | adapters/echo

HTTP adapters

Stack Module Mount helper
net/http adapters/stdlib Register(mux, opts)
Chi adapters/stdlib Mount(router, opts)
Fiber v3 adapters/fiber Register(app, opts)
Gin adapters/gin Register(router, opts)
Echo adapters/echo Register(echo, opts)

Quick Start

package main

import (
	"context"
	"log"
	"net/http"
	"path/filepath"
	"runtime"

	gouistdlib "github.com/zatrano/goui/adapters/stdlib"
	"github.com/zatrano/goui/core"
	"github.com/zatrano/goui/i18n"
	"github.com/zatrano/goui/ws"
)

type Counter struct {
	core.BaseComponent
	Count int
}

func (c *Counter) Mount(_ context.Context) error { return nil }

func (c *Counter) Render() (string, error) {
	html, err := core.RenderTemplate(`<div class="counter">
<span class="count">{{.Count}}</span>
<button type="button" g-click="increment">+</button>
<button type="button" g-click="decrement">-</button>
</div>`, c)
	if err != nil {
		return "", err
	}
	c.ResetDirty()
	return html, nil
}

func (c *Counter) HandleEvent(_ context.Context, event string, _ map[string]any) error {
	switch event {
	case "increment":
		c.Count++
	case "decrement":
		c.Count--
	}
	return nil
}

func (c *Counter) Unmount(_ context.Context) error { return nil }

func main() {
	_, file, _, _ := runtime.Caller(0)
	root := filepath.Clean(filepath.Join(filepath.Dir(file), "..", "..")) // adjust for your layout

	registry := core.NewRegistry()
	_ = registry.Register("counter", func() core.Component { return &Counter{} })

	mux := http.NewServeMux()
	mux.Handle("/client/", http.StripPrefix("/client/", http.FileServer(http.Dir(filepath.Join(root, "client")))))
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, "index.html") // HTML that loads /client/goui.js
	})

	gouistdlib.Register(mux, gouistdlib.Options{
		Server: ws.NewServer(ws.NewHub(), registry, i18n.NewTranslator()),
	})
	log.Fatal(http.ListenAndServe(":3000", mux))
}

Minimal HTML:

<div id="app"></div>
<script type="module">
  import { GoUIClient } from '/client/goui.js';
  new GoUIClient('/goui/ws', 'counter', { mount: '#app' }).connect();
</script>

Or run the shipped demo:

go run ./examples/counter
# http://localhost:3000

Repository layout

Path Role
core/ Component contract, registry, template cache
i18n/ Translator + bundled locale JSON
ws/ Session, Hub, frames, framework-agnostic Server
diff/ HTML parse, keyed diff, patches
forms/ All Tier 1 and Tier 2 controls, including searchable selects and pickers
validation/ Rule helpers
upload/ Storage + LocalStore + net/http handler
adapters/ Nested modules: stdlib, fiber, gin, echo
client/ Browser runtime + modules
examples/ Runnable demos (Fiber demos + examples/adapters/*)
docs/ Full guides (docs/en, docs/tr)

Documentation

Start with Getting started and Project integration.

Roadmap / known limits

  • Tier 3 form controls are not implemented (future / optional).
  • Upload ships with LocalStore only; upload.Storage is ready for S3/MinIO implementations.
  • Diff is optimized for typical admin UIs; always use data-key on dynamic lists.
  • Prefetch is mount-only (intentionally does not pre-send HTML).

License

MIT draft — see LICENSE. Confirm before tagging a public release.

Contributing

See CONTRIBUTING.md and CODE_OF_CONDUCT.md.

Changelog

See CHANGELOG.md (v0.1.0 = Phases 1–9).

Contact

Project: github.com/zatrano/goui Issues and PRs welcome once the repository is published.

Directories

Path Synopsis
adapters
fiber module
gin module
stdlib module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL