gowasm-bindgen

Type-safe Go in the browser.
Try the Image Processing Demo | Try the JS Sandbox
Generate TypeScript declarations and Go WASM bindings from your Go source code. Ship 90KB gzipped binaries with TinyGo.
The Problem
Go WASM functions are invisible to TypeScript:
// TypeScript has no idea what this returns or accepts
const result = window.myGoFunction(???, ???); // any
Standard Go WASM binaries are huge (~2.4MB), and WASM runs synchronously on the main thread, blocking your UI.
The Solution
gowasm-bindgen generates bindings from your normal Go functions:
// main.go - Write normal Go functions
package main
// User represents a user
type User struct {
Name string `json:"name"`
Age int `json:"age"`
Active bool `json:"active"`
}
// Greet returns a greeting message
func Greet(name string) string {
return "Hello, " + name + "!"
}
// FormatUser creates a formatted user
func FormatUser(name string, age int, active bool) User {
return User{Name: name, Age: age, Active: active}
}
Generates TypeScript with a clean, type-safe API and bindings_gen.go with WASM wrappers:
// go-wasm.ts - Generated by gowasm-bindgen
export interface FormatUserResult {
name: string;
age: number;
active: boolean;
}
export class GoWasm {
static async init(workerUrl: string): Promise<GoWasm>;
greet(name: string): Promise<string>;
formatUser(name: string, age: number, active: boolean): Promise<FormatUserResult>;
terminate(): void;
}
// bindings_gen.go - Generated Go WASM wrappers
func init() {
js.Global().Set("greet", js.FuncOf(wasmGreet))
js.Global().Set("formatUser", js.FuncOf(wasmFormatUser))
}
func wasmGreet(_ js.Value, args []js.Value) interface{} {
name := args[0].String()
return Greet(name)
}
func wasmFormatUser(_ js.Value, args []js.Value) interface{} {
name := args[0].String()
age := args[1].Int()
active := args[2].Bool()
result := FormatUser(name, age, active)
return map[string]interface{}{
"name": result.Name,
"age": result.Age,
"active": result.Active,
}
}
With TinyGo, your WASM binary drops from 2.4MB to 200KB (90KB gzipped), and Web Workers keep your UI responsive.
Quick Start
# Install
go install github.com/13rac1/gowasm-bindgen@latest
# Full build: generate bindings, copy runtime, compile WASM
gowasm-bindgen wasm/main.go
# Creates in generated/: go-wasm.ts, worker.js, wasm_exec.js, wasm.wasm
# Creates in wasm/: bindings_gen.go
# Generate only (skip WASM compilation)
gowasm-bindgen wasm/main.go --no-build
# Build with standard Go (larger binary, ~2.4MB)
gowasm-bindgen wasm/main.go --compiler go
Usage
import { GoWasm } from './generated/go-wasm';
// Worker mode (default) - non-blocking
const wasm = await GoWasm.init('./worker.js');
const greeting = await wasm.greet('World');
wasm.terminate();
// Sync mode (--mode sync)
const wasm = await GoWasm.init('./wasm.wasm');
const greeting = wasm.greet('World'); // no await needed
See the CLI Reference for all options.
Get Started
Choose your path:
See It Working
The examples/simple/ directory has a complete demo:
cd examples/simple
make all # Build WASM, generate types, verify, compile TypeScript
make serve # Open http://localhost:8080
How It Works
- Parse your Go source file
- Find exported functions (capitalized names with no receiver)
- Extract parameter names and types from function signatures
- Extract return types, supporting structs with JSON tags and (T, error) patterns
- Generate TypeScript client with a type-safe API
- Generate
bindings_gen.go with WASM wrapper functions that handle type conversions
- Generate
worker.js for async/non-blocking calls (default) or sync mode with --mode sync
- Copy
wasm_exec.js runtime from your Go/TinyGo installation
- Compile WASM binary with TinyGo (or standard Go with
--compiler go)
No annotations. No build plugins. Just normal Go code.
Requirements
- Go 1.21+
- TinyGo (recommended for small binaries) or standard Go
- Node.js 18+ (for TypeScript verification and example demo)
- Write normal exported Go functions - the tool generates the WASM wrappers for you
Note: TinyGo produces much smaller binaries but has language limitations. Use standard Go if you need full reflection or unsupported features.
Supported Types
Primitives, slices, maps, structs (with JSON tags), errors, and pointers. See Type Mapping for the full conversion table.
Limitations
See LIMITATIONS.md for a comparison with Rust's wasm-bindgen and current gaps. Highlights:
- Worker mode is default (async Promise-based), use
--mode sync for synchronous blocking calls
- Void callbacks supported (fire-and-forget), no return value callbacks
- Typed arrays for byte slices, element-by-element for other numeric slices
- Class-based API (methods on class instances)
- gowebapi/webapi — Go bindings for browser APIs (DOM, Fetch, Canvas, etc.). Use it alongside gowasm-bindgen: gowebapi/webapi lets your Go code call browser APIs, while gowasm-bindgen lets JavaScript call your Go functions.
License
MIT
Thanks
Logo assets sourced from: