Contents

GoUI can deliver the same component three ways. Pick the mode at registration time — routes stay one-liners.

ModeFirst HTTP responseWebSocketTypical use
ModeLive (default)Empty #app shell + client scriptYesAdmin, ERP, dashboards
ModeSEOFull HTML body + <head> meta + client scriptYes (hydrates)Marketing, blog, product pages
ModeStaticFull HTML body + meta, no client scriptNoLegal, about, pure content

Register

registry.Register("orders", NewOrders) // ModeLive

registry.RegisterPage("landing", NewLanding, core.ModeSEO)
registry.RegisterPage("privacy", NewPrivacy, core.ModeStatic)

Register is unchanged and always ModeLive.

Optional Head metadata

Implement core.HeadProvider on SEO/Static components:

func (l *Landing) Head() core.Head {
    return core.Head{
        Title:       "Acme — Home",
        Description: "…",
        Canonical:   "https://example.com/",
        OGTitle:     "Acme",
        OGImage:     "https://example.com/og.png",
    }
}

Mount the page renderer

import "github.com/zatrano/goui/page"

renderer := page.NewRenderer(page.Options{
    Registry:   registry,
    Translator: translator,
})

// Fiber (same idea for Gin / Echo / stdlib)
gouifiber.Register(app, gouifiber.Options{
    Server: server,
    Page:   renderer,
    Routes: []page.Route{
        {Path: "/", Component: "landing"},
        {Path: "/about", Component: "privacy"},
        {Path: "/admin", Component: "orders"},
    },
})

Or mount a single path:

app.Get("/product", gouifiber.Page(renderer, "product"))
// net/http:
mux.Handle("/product", renderer.Handler("product"))

Request access in Mount

SEO/Static handlers put *http.Request on the context:

func (p *Product) Mount(ctx context.Context) error {
    req := core.RequestFromContext(ctx)
    // req.URL.Query(), path, headers…
    return nil
}

How ModeSEO hydrates

  1. GET returns HTML with data-goui-component="ssr" and data-goui-ssr="1".
  2. goui.js opens the WebSocket and receives the first full render.
  3. The client adopts the existing DOM (no flash), remaps the component id, then applies later patches as usual.

ModeLive still works with a hand-written index.html if you prefer; the page renderer is optional.

Example

See examples/seo-pages: / (SEO), /about (Static), /admin (Live).