Hello World (Go)
This guide creates a minimal Electrobun app with a Go main process and a
normal project-owned Go module. For the default
TypeScript runtime see Hello World. The
faster path is hutch electrobun init go-app --template=go-maze-wgpu,
which scaffolds a complete WGPU-rendering example.
1. Create the project
mkdir hello-gocd hello-goCreate hutch.config.ts. Hutch resolves scripts only from this file; this
project does not need a package.json:
export default { electrobun: { version: "2.0.1-beta.0" }, scripts: { dev: ["hutch", "electrobun", "dev", "--watch"], build: ["hutch", "electrobun", "build", "--env=stable"], },};Create tsconfig.json so editors and TypeScript resolve the config types and
view SDK projected during sync:
{ "extends": "./.hutch/devkit/tsconfig.json"}Create go.mod:
module hellogo.example.dev
go 1.26.0
require electrobun v0.0.0
replace electrobun => ./.hutch/devkit/go-sdkThe replacement is the Go SDK copied from the exact Electrobun devkit.
Hutch does not generate this module or resolve other Go dependencies; add them
normally and let Go own go.mod and go.sum.
2. Add a webview
Create src/mainview/index.html:
<!doctype html><html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width" /> <title>Hello Electrobun</title> </head> <body> <h1>Hello from Electrobun</h1> <script type="module" src="index.js"></script> </body></html>Create src/mainview/index.ts — the view’s script entrypoint, bundled to
views://mainview/index.js:
console.log("Hello from the view");3. Add the main process
Create src/go/main.go:
package main
import ( "fmt" "os" "time"
"electrobun")
func main() { if err := run(); err != nil { fmt.Fprintf(os.Stderr, "[hello-go] %s\n", err) os.Exit(1) }}
func run() error { core, err := electrobun.LoadCore() if err != nil { return err } bundlePaths, err := electrobun.ResolveBundlePaths() if err != nil { return err } appInfo, err := electrobun.ResolveAppInfoFromBundle(bundlePaths) if err != nil { return err }
go createUI(core, bundlePaths)
// Blocks on the native event loop; must run on the main goroutine. return core.RunMainThread(appInfo)}
func createUI(core *electrobun.Core, bundlePaths electrobun.BundlePaths) { time.Sleep(150 * time.Millisecond) if err := core.ConfigureWebviewRuntimeFromExecutableDir(bundlePaths, 0); err != nil { fmt.Fprintf(os.Stderr, "[hello-go] failed to configure webview runtime: %s\n", err) return }
windowOptions := electrobun.NewWindowOptions( "Hello Electrobun", electrobun.NewRect(140, 100, 800, 600), ) windowOptions.Callbacks = electrobun.WindowCallbacks{ Close: func(uint32) { _ = core.StopEventLoop() }, } windowID, err := core.CreateWindow(windowOptions) if err != nil { fmt.Fprintf(os.Stderr, "[hello-go] failed to create window: %s\n", err) return }
webviewOptions := electrobun.NewWebviewOptions( windowID, "views://mainview/index.html", electrobun.NewRect(0, 0, 800, 600), ) if _, err := core.CreateWebview(webviewOptions); err != nil { fmt.Fprintf(os.Stderr, "[hello-go] failed to create webview: %s\n", err) _ = core.CloseWindow(windowID) }}A few things to notice: RunMainThread owns the main goroutine, so all UI
creation happens on a separate goroutine; the window’s Close callback
stops the event loop so the process exits when the window closes; and nil
webview callbacks are fine — a nil DecideNavigation allows all
navigation.
4. Configure the build
Create electrobun.config.ts:
import type { ElectrobunConfig } from "electrobun";
export default { app: { name: "hello-go", identifier: "hellogo.example.dev", version: "0.0.1", }, build: { mainProcess: "go", go: { package: "./src/go", }, views: { mainview: { entrypoint: "src/mainview/index.ts", }, }, copy: { "src/mainview/index.html": "views/mainview/index.html", }, },} satisfies ElectrobunConfig;mainProcess: "go" selects the Go runtime, and go.package selects the main
package within the project module. Per-platform bundleCEF / bundleWGPU
flags are only needed if you use CEF rendering or WGPU views.
5. Run the app
hutch electrobun synchutch run devThe explicit sync projects the Go SDK and resolves the selected Go toolchain
when no exact match is installed. On Windows it also prepares the Zig C
compiler required by cgo. hutch run dev then builds and launches the app and
rebuilds it when watched source files change. Press Ctrl+C to stop it.
How this differs from TypeScript
- Normal module ownership. Hutch runs
go buildfrom the project module with-mod=readonly. It does not copy source or synthesize a GOPATH. The project ownsgo.mod,go.sum, module replacements, and third-party dependencies. - Version-matched SDK.
hutch electrobun syncprojects the selected release’s module into.hutch/devkit/go-sdk; the explicitreplacedirective makes the SDK import deterministic. - cgo is mandatory. The SDK is a cgo wrapper, so the build runs with
CGO_ENABLED=1and needs a host C toolchain. - Dynamic core loading.
LoadCore()dlopenslibElectrobunCorefrom the executable’s directory at runtime and resolves its symbols. Nothing links at build time, and the binary only runs from inside a hutch-built bundle — not viago run. - Bundle discovery.
ResolveBundlePaths()derives the executable andResources/directories fromos.Executable(), andResolveAppInfoFromBundle()reads the app identifier, name, and channel fromResources/version.json. - Webview runtime setup. Call
ConfigureWebviewRuntimeFromExecutableDironce before creating any webview; it registers the preload scripts that power theviews://scheme and the bridge. - You own the event loop.
RunMainThreadmust be called from the main goroutine and blocks until the loop stops. Create windows and webviews from another goroutine after a short delay (~150 ms) so the loop is running first, and callStopEventLoop()from the window close callback or the process keeps running. - Errors are plain
errorvalues. Every core call returns anerror;core.LastError()surfaces the core’s last error string (electrobun_core_last_error). - Template.
hutch electrobun init go-app --template=go-maze-wgpuscaffolds a full example with RPC messaging and WGPU rendering.
Toolchain
Electrobun 2.0.0 defaults to Go 1.26.4. Override it exactly with
build.go.version; Hutch uses an exact matching system compiler when supported
or downloads it under ~/.hutch/toolchains/go/<version>/<platform>. Builds
target the host OS and architecture only — cross-compilation is not
supported yet, so use native runners per platform. Because of cgo you need
a host C toolchain: the Xcode Command Line Tools on macOS, the platform
compiler and linker on Linux, and on Windows the build uses the vendored
zig cc as the C compiler.
For the full picture of native main processes, see Native main process and the main process APIs.