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 client.ts with a clean, type-safe API and bindings_gen.go with WASM wrappers:
// client.ts - Generated by gowasm-bindgen
export interface FormatUserResult {
name: string;
age: number;
active: boolean;
}
export class Main {
static async init(workerUrl: string): Promise<Main>;
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
# Generate TypeScript client and Go bindings
gowasm-bindgen main.go --output client.ts --go-output bindings_gen.go
# Creates: client.ts (async API) + worker.js (Web Worker) + bindings_gen.go (Go wrappers)
# Or generate synchronous API (blocks main thread)
gowasm-bindgen main.go --output client.ts --go-output bindings_gen.go --sync
# Creates: client.ts only (sync API) + bindings_gen.go (Go wrappers)
# Or just TypeScript without Go bindings
gowasm-bindgen main.go --output client.ts
# Creates: client.ts + worker.js (you write WASM wrappers manually)
Usage
import { Main } from './client';
// Worker mode (default) - non-blocking
const wasm = await Main.init('./worker.js');
const greeting = await wasm.greet('World');
wasm.terminate();
// Sync mode (--sync flag)
const wasm = await Main.init('./example.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
client.ts with a type-safe TypeScript 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 --sync
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
--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: