via

You are reading the docs for v0.8. Read the latest (v0.10)

via

Web UI in Go that stays live

A page is a struct, a click is a method call, and via streams what changed back to the tab. No JavaScript to write, no build step, plain HTTP until something on the page ticks.

#A counter

The struct is the page and each button calls a method. The count is one *atomic.Int64 shared by every visitor, so the number you see includes everyone else's clicks.

main.go
type Counter struct{ n *atomic.Int64 }

func (c *Counter) Inc(ctx *via.Ctx) { c.n.Add(1) }
func (c *Counter) Dec(ctx *via.Ctx) { c.n.Add(-1) }

func (c *Counter) View() h.H {
	return h.Div(
		h.Button(on.Click(c.Dec), h.Str("-")),
		h.H1(h.Str(c.n.Load())),
		h.Button(on.Click(c.Inc), h.Str("+")),
	)
}

#Running

The same counter on this page. Nothing in its View is live, so a click is one POST and its response is the patch.

0

Try this

  • Click + and read the Wire pane: the POST, its status, the patched element.
Wire

Use the demo to see its requests and patches here.

Source
package demos

import (
	"sync/atomic"

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

// LandingCounter is the front page's counter as it runs there. Nothing in its
// View is live, so a click is one POST whose response is the patch.
type LandingCounter struct{ n *atomic.Int64 }

var landingCount atomic.Int64

func NewLandingCounter() LandingCounter { return LandingCounter{n: &landingCount} }

func resetLanding() { landingCount.Store(0) }

func (c *LandingCounter) Inc(ctx *via.Ctx) { c.n.Add(1) }
func (c *LandingCounter) Dec(ctx *via.Ctx) { c.n.Add(-1) }

func (c *LandingCounter) View() h.H {
	return h.Div(h.Class("row"),
		h.Button(on.Click(c.Dec), h.Str("−")),
		h.Output(h.Str(c.n.Load())),
		h.Button(on.Click(c.Inc), h.Str("+")),
	)
}

See the full program →

#Three kinds of field

Field typeValue lives inServer code
Signalthe browser; sent with every action POSTreads it with Get, changes it with Set
SignalCSthe browser only; dropped from every POSTcannot read or write it: the type has no Get or Set
Statethe server, per tabowns it; rendering it with Display makes the unit live, and a Set is pushed

Where a value lives is the type of its field, so the compiler holds the client/server split: server code that reads a via.SignalCS does not build, and a via.State has no Ref for a client expression to reach. The full table is Where state lives, and Why Via makes the longer case.