go-app Documentation
repository·master·Indexed 27 days ago
https://github.com/maxence-charriere/go-appA framework for building SEO-friendly, offline-capable Progressive Web Apps (PWA) using Go and WebAssembly (Wasm). It features a declarative, component-based UI syntax, integrated HTTP and component routing, and a diffing algorithm for efficient DOM updates. The toolkit includes utilities for CLI configuration via struct tags and an enriched error handling package.
What's inside go-app
- go-app is a Go package designed for building Progressive Web Apps (PWA) using Go and WebAssembly (Wasm). It allows developers to shape UIs using a declarative syntax that creates and composes HTML elements directly within Go code. Applications built with go-app are served using the standard Go HTTP model, making them SEO friendly, installable, and capable of supporting offline mode.
Understand the go-app lifecycle and PWA loading scenarios
masterApps built with
go-appare WebAssembly binaries served via HTTP and function as Progressive Web Apps (PWAs) using service workers and caching. There are three primary loading scenarios:- First loading: The initial visit where the page, service worker, and app resources (
app.wasm, CSS, JS) are downloaded and cached. - Recurrent loadings: Subsequent visits where the service worker is compared to the cached version. If they are identical, resources are loaded directly from the browser cache.
- Loading after an app update: Occurs when the live service worker differs from the cached version. The new page and resources are downloaded and cached.
Note: Even after an update is downloaded in the background, the user must reload the page to see the modifications, as the current view is still running the cached version.
- First loading: The initial visit where the page, service worker, and app resources (
Manage Static Resources and app.wasm
masterStatic resources (CSS, JS, images, etc.) and the
app.wasmbinary are stored in awebdirectory.- app.wasm: Must always be located at
/web/app.wasm. - Static Resources: Located at
/web/RESOURCE_NAME.
These files can be served by the Go server using the
Handleror hosted on remote storage like AWS S3 or Google Cloud Storage.- app.wasm: Must always be located at
Create UI components using Declarative Syntax
masterGo-app allows you to build reusable, component-based UI elements using pure Go. You define a struct that embeds
app.Compoand implement theRender() app.UImethod. InsideRender, you compose the UI using functional builders (likeapp.Div(),app.H1(),app.Text()) and control flow elements likeapp.If()andapp.Else(). Data binding is achieved through methods like.Value()for state and.OnChange()for event handling.// A component that displays a Hello world by composing with HTML elements, // conditions, and binding. type hello struct { app.Compo name string } func (h *hello) Render() app.UI { return app.Div().Body( app.H1().Body( app.Text("Hello, "), app.If(h.name != "", func() app.UI { return app.Text(h.name) }).Else(func() app.UI { return app.Text("World!") }), ), app.P().Body( app.Input(). Type("text"). Value(h.name). Placeholder("What is your name?"). AutoFocus(true). OnChange(h.ValueTo(&h.name)), ), ) }Send a push notification from a server
masterTo trigger a push notification, your server must send a JSON-encoded
app.Notificationstruct to the endpoint provided in the user's subscription.Requirement: The notification message must be a JSON-encoded
app.Notificationobject.Example fields in the JSON payload:
Title: The title of the notification.Body: The main text content.Path: The application path to navigate to when the notification is clicked.
// Example of creating the JSON payload for a notification body, _ := json.Marshal(app.Notification{ Title: "Push test from server", Body: "go-app push notification number", Path: "/mypage", }) // This payload is then sent to the push service (e.g., using webpush-go) res, err := webpush.SendNotification(body, &sub, &webpush.Options{ VAPIDPrivateKey: h.VAPIDPrivateKey, VAPIDPublicKey: h.VAPIDPublicKey, TTL: 30, })Customize Page Metadata for SEO
masterTo improve SEO, you should set metadata like the page title and author. This is done within the
OnPreRendermethod using thePagefield of theapp.Contextargument.func (h *hello) OnPreRender(ctx app.Context) { ctx.Page.SetTitle("A Hello World written with go-app") ctx.Page.SetAuthor("Maxence") }Test component client lifecycle
masterTo test client-side behaviors like mounting (
OnMount) or navigation (OnNav), useapp.NewClientTester(). This emulates the web browser environment.Key steps:
- Create a tester with
app.NewClientTester(compo). - Use
disp.Nav(&url.URL{})to simulate navigation. - Call
disp.Consume()to process the resulting UI changes. - Always call
defer disp.Close()to release allocated resources.
type aTitle struct { app.Compo title string } func (t *aTitle) OnMount(ctx app.Context) { t.title = "Testing Mounting" } func (t *aTitle) OnNav(ctx app.Context) { t.title = "Testing Nav" } func (t *aTitle) Render() app.UI { return app.H1(). Class("title"). Text(t.title) } func TestComponentLifcycle(t *testing.T) { compo := &aTitle{} disp := app.NewClientTester(compo) defer disp.Close() disp.Nav(&url.URL{}) disp.Consume() if compo.title != "Testing Nav" { t.Fatal("bad component title:", compo.title) } }- Create a tester with
Migrate from go-app v8 to v9
masterThe transition from v8 to v9 introduces breaking changes focused on making the package more reactive. Key changes include the removal of manualcompo.Update()calls (components now auto-update on lifecycle events, HTML events, or dispatches) and the requirement of Go 1.18 or higher.Serve a go-app using the Standard HTTP Server
masterGo-app integrates with the Go standard
net/httpmodel. To serve an application, you use&app.Handler{}as anhttp.Handler. You can also define client-side routing usingapp.Route(path, composerFunc)and then callapp.RunWhenOnBrowser()to start the client-side execution. On the server side, usehttp.ListenAndServeto host the application.func main() { // Go-app component routing (client-side): app.Route("/", func() app.Composer { return &hello{} }) app.Route("/hello", func() app.Composer { return &hello{} }) app.RunWhenOnBrowser() // Standard HTTP routing (server-side): http.Handle("/", &app.Handler{ Name: "Hello", Description: "An Hello World! example", }) if err := http.ListenAndServe(":8000", nil); err != nil { log.Fatal(err) } }Use the go-app declarative syntax for UI composition
mastergo-app uses a chaining mechanism based on Go syntax to compose HTML elements and components. You define your UI by implementing the
Render() app.UImethod on a component struct. Elements are created using functions named after the HTML tag (e.g.,app.Div(),app.H1()) and configured via method chaining.func (c *myCompo) Render() app.UI { return app.Div().Body( app.H1(). Class("title"). Text("Build a GUI with Go"), app.P(). Class("text"). Text("Just because Go and this package are really awesome!"), ), }Configure HTML element attributes and styles
masterHTML element interfaces provide methods to set attributes and styles using method chaining.
- Attributes: Use methods like
Class(v string),ID(v string), orType(v string)to set standard attributes. You can chain multiple calls to set multiple attributes. - Styles: Use
Style(k, v string)to set CSS properties. Multiple styles can be chained. - Nesting: Use the
Body(children ...UI)method to nest other elements, texts, or components inside standard elements.
// Setting multiple attributes and styles return app.Div(). ID("id-name"). Class("class-1"). Class("class-2"). Style("width", "400px"). Style("height", "200px"). Style("background-color", "deepskyblue")- Attributes: Use methods like
Migrate from V7 to V8
masterGo-app V8 introduces server-side prerendering for SEO, which requires several breaking changes to your codebase. Key migration steps include:
- Update Imports: Replace all
github.com/maxence-charriere/go-app/v7/pkg/appimports withgithub.com/maxence-charriere/go-app/v9/pkg/app(or the appropriate V8 version). - Merge Build Directives: V8 no longer requires separate
// +build wasmand server-side files. You should merge your client-sidemain()and server-sidemain()into a single file. Useapp.RunWhenOnBrowser()for the client-side logic and standardhttp.ListenAndServefor the server-side logic. - Update Routing: Calls to
app.Route()andapp.RouteWithRegexp()must now be present in the server-side code as well. Note that these functions now register the type of a component rather than an instance; a fresh instance is created upon navigation. Use component lifecycle interfaces (likeMounter) for initialization. - Update Concurrency: Instead of launching raw goroutines from components, use
Context.Async()to ensure proper lifecycle management.
- Update Imports: Replace all