--- title: Binding System description: How Wails v3 lets Go and JavaScript call each other with zero boilerplate sidebar: order: 5 --- > “Bindings” are the **type-safe contract** that lets you write: ```go msg, err := chatService.Send("Hello") ``` in Go *and* ```ts const msg = await window.backend.ChatService.Send("Hello") ``` in TypeScript **without manual glue code**. This document breaks down *how* that magic happens, from **static analysis** at build time, through **code generation**, to the **runtime bridge** that moves bytes over the WebView. --- ## 1. 30-Second Overview | Stage | Component | Output | |-------|-----------|--------| | **Analysis** | `internal/generator/analyse.go` | In-memory AST of exported Go structs, methods, params, return types | | **Generation** | `internal/generator/render/*.tmpl` | • `pkg/application/bindings.go` (Go)
• `frontend/src/wailsjs/**.ts` (TS defs)
• `runtime/desktop/@wailsio/runtime/internal/bindings.json` | | **Runtime** | `pkg/application/messageprocessor_call.go` + JS runtime (`invoke.ts`) | JSON messages over WebView native bridge | The flow is orchestrated by the `wails3` CLI: ``` wails3 generate ─┬─> generator.Collect() // parse Go packages ├─> generator.Analyse() // build semantic model └─> generator.Render() // write files + `go fmt` ``` --- ## 2. Static Analysis ### Entry Point ``` internal/generator/analyse.go ``` `Analyse(cfg generator.Config)`: 1. Recursively loads provided Go packages with `go/packages`. 2. Walks the *type* and *value* specs of every AST file. 3. Filters **exported** symbols (capitalised) outside `internal/` packages. 4. Records: * Receiver type (`struct`, `interface`) * Method name * Parameter list (name, type, pointer, variadic) * Return tuple (values + error presence) 5. Normalises types to a **serialisable set** (`int`, `float64`, `string`, `[]byte`, slices, maps, structs, pointers). Unsupported types produce a **compile-time error** so mistakes are caught early. ### Model ```go type Method struct { ID uint32 // stable hash for runtime lookup Name string Params []Param Results []Result Receiver *Struct // nil for package funcs } ``` A deterministic **FNV-1a** hash (`internal/hash/fnv.go`) of `.()` becomes the *method ID* used at runtime. --- ## 3. Code Generation ### Templates `internal/generator/render/` contains text/template files: | Template | Target | |----------|--------| | `go_bindings.tmpl` | `pkg/application/bindings.go` | | `ts_bindings.tmpl` | `frontend/wailsjs/go/models.d.ts` | | `ts_index.tmpl` | `frontend/wailsjs/index.ts` | Add or adjust templates here to customise generation. ### Go Output `bindings.go` registers a lookup table: ```go var bindings = []application.BoundMethod{ {ID: 0x7A1201D3, Name: "ChatService.Send", Call: chatServiceSend}, } func chatServiceSend(ctx context.Context, in []byte) ([]byte, error) { var req struct{ Msg string } if err := json.Unmarshal(in, &req); err != nil { return nil, err } res, err := chatService.Send(req.Msg) return json.Marshal(res), err } ``` Key points: * **Zero reflection** at runtime → performance. * Marshal/Unmarshal is **per-method** with generated struct wrappers. ### TypeScript Output ```ts export namespace backend { export namespace ChatService { function Send(msg: string): Promise; } } ``` * Emitted as **ES modules** so any bundler can tree-shake. * Method IDs are embedded in an auto-generated `bindings.json` for the JS runtime. --- ## 4. Runtime Invocation Protocol ### JavaScript Side ```ts import { System } from "@wailsio/runtime"; await System.invoke(0x7a1201d3 /* ChatService.Send */, ["Hello"]); ``` Implementation: `runtime/desktop/@wailsio/runtime/invoke.ts` 1. Packs `{t:"c", id:, p:[...args]}` into JSON. 2. Calls `window.external.invoke(payload)` (WebView2) or equivalent. 3. Returns a `Promise` that resolves/rejects based on the reply. ### Go Side 1. `messageprocessor_call.go` receives the JSON. 2. Looks up `methodID` in the `bindings` slice. 3. Executes the generated stub (`chatServiceSend`). 4. Serialises `{result, error}` back to JS. ### Error Mapping | Go | JavaScript | |----|------------| | `error == nil` | `Promise` resolves with result | | `errors.New(...)` | `Promise` rejects with `{message, stack, code}` | The mapping code lives in `runtime/desktop/@wailsio/runtime/errors.ts`. --- ## 5. Calling JavaScript from Go *Browser → Go* is covered above. *Go → Browser* uses **Events** or **Eval**: ```go window.Eval(`alert("Hi")`) app.Publish("chat:new-message", msg) ``` Binding generation is one-way (Go methods). For JS-exposed functions use `runtime.EventsOn` in JS and `application.Publish` from Go. --- ## 6. Extending & Troubleshooting ### Adding Custom Serialisers * Implement `generator.TypeConverter` interface. * Register in `generator.Config.Converters`. * Update JS runtime deserialisation if needed. ### Unsupported Type Error ``` error: field "Client" uses unsupported type: chan struct{} ``` → wrap the channel in a struct with an exposed API or redesign. ### Version Skew Bindings are regenerated on **every** `wails3 dev` / `wails3 build`. If IDE intellisense shows stale stubs, delete `frontend/wailsjs` and rebuild. ### Performance Tips * Prefer **value** receivers for small structs to reduce allocations. * Avoid large byte slices over the bridge; use `application.FileServer` instead. * Batch multiple quick calls into one method to minimise bridge latency. --- ## 7. Key Files Map | Concern | File | |---------|------| | Static analysis entry | `internal/generator/analyse.go` | | Render pipeline | `internal/generator/generate.go` | | Template assets | `internal/generator/render/*.tmpl` | | Go binding table | `pkg/application/bindings.go` (generated) | | Call processor | `pkg/application/messageprocessor_call.go` | | JS runtime | `runtime/desktop/@wailsio/runtime/invoke.ts` | | Errors mapping | `runtime/desktop/@wailsio/runtime/errors.ts` | Keep this cheat-sheet handy when you trace a bridge bug. --- ## 8. Recap 1. **Generator** scans your Go code → semantic model. 2. **Templates** emit **Go stubs** + **TypeScript definitions**. 3. **Message Processor** executes stubs at runtime. 4. **JS Runtime** wraps it all in idiomatic promises. All without reflection, without IPC servers, and without you writing a single line of boilerplate. That’s the Wails v3 binding system. Go forth and bind!