Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

go-via/via

Open more actions menu

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

227 Commits
227 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Via — reactive web apps in pure Go

Via

Go Reference Go Report Card CI License: MIT Docs

Reactive web apps in pure Go. A composition is a struct, reactive state is a typed field, actions are methods — and the compiler understands your UI. Via is the only framework, in any language, that expresses the client/server reactive split as a Go type: Signal[T] lives in the browser, StateTab/Sess/App[T] live only on the server. Which side owns a piece of state is a field declaration the compiler checks, not a convention you grep for. Transport is SSE only — no WebSockets, no build step, no hand-written JS.

📖 Documentation · API reference · Examples

Install

go get github.com/go-via/via

Quickstart: the counter

Two counters, two scopes. Local is per-tab server state; Shared is one value across every session — clicking +1 bumps Local only in that tab, but Shared everywhere at once. No Broadcast, no WebSocket, no client JS. on.Click(p.IncShared) is a typed method reference: the handler signature is compile-checked and a misspelled method name won't build. It must be a real bound method, though — a closure or plain function satisfies the type but has no name to route to, so it panics at the first render rather than at compile time.

package main

import (
    "net/http"

    "github.com/go-via/via"
    "github.com/go-via/via/h"
    "github.com/go-via/via/on"
)

type Page struct {
    Local  via.StateTabNum[int] // per-tab — independent in every tab
    Shared via.StateAppNum[int] // global — synced across every session
}

func (p *Page) IncLocal(ctx *via.Ctx)  { p.Local.Op(ctx).Inc() }
func (p *Page) IncShared(ctx *via.Ctx) { p.Shared.Op(ctx).Inc() }

func (p *Page) View(ctx *via.CtxR) h.H {
    return h.Div(
        h.P(h.Text("Local: "), p.Local.Text(ctx)),
        h.Button(h.Text("+1"), on.Click(p.IncLocal)),
        h.P(h.Text("Shared: "), p.Shared.Text(ctx)),
        h.Button(h.Text("+1"), on.Click(p.IncShared)),
    )
}

func main() {
    app := via.New()
    via.Mount[Page](app, "/")
    _ = http.ListenAndServe(":3000", app)
}
go run ./internal/examples/counterscope   # open in two browsers

Two browsers, two scopes — StateTab is per-tab, StateApp is shared across every session.

For state shared across users, see the live chatroom — one app-scoped field that fans every message out to every connected tab: internal/examples/chat · tutorial.

The four reactive shapes

Whether state lives on the client, the server, or both is the field's type:

Handle Scope Lives on
via.Signal[T] per-tab client + server
via.StateTab[T] per-tab server only
via.StateSess[T] per-session server only
via.StateApp[T] global server only

Read(ctx) / Update(ctx, fn) everywhere; Signal and StateTab add Write(ctx, v). The Num / Bool / Str / Slice / Map wrappers add typed Op(ctx) verbs (Add, Toggle, Append, …). Full model →

What Via is — and is not

  • Is: server-rendered pages with typed end-to-end state, a reactive browser runtime (Datastar — it keeps the page reactive and updates it in place), and no build step — best for internal tools, dashboards, and line-of-business apps you'd otherwise build with LiveView, Hotwire, or htmx + hand-written JS.
  • Is not an SPA framework — the browser receives HTML, not a JSON bundle.
  • Single-process by default — without a backplane StateApp[T] and Broadcast are per-pod and horizontal scaling needs sticky sessions. WithBackplane (in preview) converges StateApp state across pods and fans Broadcast out to every pod's tabs; both inherit the backplane's pre-1.0 status.
  • Is not offline-first or stable yet — drop the SSE stream and the tab freezes until the client reconnects (transient drops retry automatically; a clean-close deploy may fall back to a reload), and APIs can still shift pre-1.0.

Documentation

The full guide and reference live at go-via.github.io/via.

License

MIT

Morty Proxy This is a proxified and sanitized view of the page, visit original site.