--- title: Extending Wails description: Practical guide to adding new features and platforms to Wails v3 sidebar: order: 8 --- > Wails is designed to be **hackable**. > Every major subsystem lives in Go code you can read, modify, and ship. > This page shows _where_ to start and _how_ to stay cross-platform when you: * Add a **Service** (file server, KV store, custom IPC…) * Create a **new CLI command** (`wails3 `) * Extend the **Runtime** (window API, dialogs, events) * Introduce a **Platform capability** (Wayland, Apple Vision OS…) * Keep **cross-platform compatibility** without drowning in `//go:build` tags --- ## 1. Adding a Service Services are background Go components exposed to apps via the `application.Context`. ``` internal/service/ ├── template/ # Boilerplate for new services │ ├── template.go │ └── README.md └── service.go # Registration + lifecycle interfaces ``` ### 1.1 Define the Service ```go package chat type Service struct { messages []string } func New() *Service { return &Service{} } func (s *Service) Send(msg string) string { s.messages = append(s.messages, msg) return "ok" } ``` ### 1.2 Implement `service.Service` ```go func (s *Service) Init(ctx *application.Context) error { return nil } func (s *Service) Shutdown() error { return nil } ``` ### 1.3 Register Generator *Add to* `internal/generator/collect/services.go` ```go services.Register("chat", chat.New) ``` > Now `wails3 generate` emits bindings so JS can call > `window.backend.chat.Send("hi")`. ### 1.4 Ship an Example Copy `v3/examples/services/` and adjust. --- ## 2. Writing a New CLI Command CLI logic lives under `internal/commands/`. Each command is **one file** that mounts itself in `init()`. ### 2.1 Create the File `v3/internal/commands/hello.go` ```go package commands import ( "github.com/spf13/cobra" ) func init() { Add(&cobra.Command{ Use: "hello", Short: "Prints Hello World", RunE: func(cmd *cobra.Command, args []string) error { cmd.Println("Hello Wails!") return nil }, }) } ``` `Add()` registers with the root in `cmd/wails3/main.go`. ### 2.2 Re-compile CLI ``` go install ./v3/cmd/wails3 wails3 hello ``` If your command needs **Taskfile** integration, reuse helpers in `internal/commands/task_wrapper.go`. --- ## 3. Modifying the Runtime Common reasons: * New window feature (`SetOpacity`, `Shake`, …) * Extra dialog (`ColorPicker`) * System-level API (screen brightness) ### 3.1 Public API Add to `pkg/application/window.go`: ```go func (w *WebviewWindow) SetOpacity(o float32) { w.ctx.Call("window:setOpacity", o) } ``` ### 3.2 Message Processor Create `messageprocessor_window_opacity.go`: ```go const MsgSetOpacity = "window:setOpacity" func init() { register(MsgSetOpacity, handleSetOpacity) } func handleSetOpacity(ctx *Context, params json.RawMessage) ([]byte, error) { var req struct { ID uint64 `json:"id"` Opacity float32 `json:"o"` } json.Unmarshal(params, &req) return nil, runtime.SetOpacity(req.ID, req.Opacity) } ``` ### 3.3 Platform Implementation ``` internal/runtime/ webview_window_darwin.go webview_window_linux.go webview_window_windows.go ``` Add `SetOpacity()` to each, guarded by build tags. If a platform can’t support it, return `ErrCapability`. ### 3.4 Capability Flag Expose via `internal/capabilities`. ```go const CapOpacity = "window:opacity" ``` Runtime files `*_darwin.go` etc. should `registerCapability(CapOpacity)` when successfully initialised. Apps can query: ```go if application.HasCapability(application.CapOpacity) { ... } ``` --- ## 4. Adding New Platform Capabilities Example: **Wayland** clipboard on Linux. 1. Fork `internal/runtime/clipboard_linux.go`. 2. Split file: * `clipboard_linux_x11.go` → `//go:build linux && !wayland` * `clipboard_linux_wayland.go` → `//go:build linux && wayland` 3. Add CLI flag `--tags wayland` in `internal/commands/dev.go` (`goEnv.BuildTags += ",wayland"`). 4. Document in **Asset Server** & README. > Keep default build tags minimal; reserve opt-in tags for niche features. --- ## 5. Cross-Platform Compatibility Checklist | ✅ Step | Why | |---------|-----| | Provide **every** public method in all platform files (even if stub) | Keeps build green on all OS/arch | | Gate platform specifics behind **capabilities** | Let apps degrade gracefully | | Use **pure Go** first, CGO only when needed | Simplifies cross-compiles | | Test with `task matrix:test` | Runs `go test ./...` on linux/mac/windows in Docker | | Document new build tags in `docs/getting-started/installation.mdx` | Users must know flags | --- ## 6. Debug Builds & Iteration Speed * `wails3 dev -tags debug` adds extra log hooks (`logger_dev*.go`). * Use `task reload` to rebuild runtime without restarting the whole app. * Race detector: `wails3 dev -race` (see `pkg/application/RACE.md`). --- ## 7. Upstream Contributions 1. Open an **issue** to discuss the idea & design. 2. Follow the techniques above to implement. 3. Add: * Unit tests (`*_test.go`) * Example (`v3/examples//`) * Docs (this file or API reference) 4. Ensure `go vet`, `golangci-lint`, and CI matrix pass. --- ### Quick Links | Area | Location | |------|----------| | Services framework | `internal/service/` | | CLI core | `internal/commands/` | | Runtime per-OS | `internal/runtime/` | | Capability helpers| `internal/capabilities/` | | Taskfile DSL | `tasks/Taskfile.yml` | | Dev matrix tests | `tasks/events/generate.go` | --- You now have a **roadmap** for bending Wails to your will—add services, sprinkle CLI magic, hack the runtime, or bring entirely new OS features. Happy extending!