WebAssembly Overview (see WebAssembly & TinyGo section)
Go can target WebAssembly without cgo: the browser path uses GOOS=js GOARCH=wasm with syscall/js, and the server/edge path uses GOOS=wasip1 GOARCH=wasm for WASI hosts.
cgo is unavailable on wasm targets, so interop shifts to JavaScript bridges, WASI imports, or TinyGo for constrained builds.
Summary
WebAssembly is a portable alternative to native cgo when you deploy to browsers, edge runtimes, or sandboxed plugins.
Full build pipelines, size tuning, and host comparisons live in the WebAssembly & TinyGo section - this page orients cgo readers on when to pivot.
Recipe
Quick-reference recipe card - copy-paste ready.
# Browser wasm (syscall/js)
GOOS=js GOARCH=wasm go build -o app.wasm ./cmd/app
# WASI preview1 (servers, wazero, wasmtime)
GOOS=wasip1 GOARCH=wasm go build -o app.wasm ./cmd/appWhen to reach for this:
- You need Go in the browser without native plugins.
- Edge platforms execute
.wasmwith WASI instead of libc. CGO_ENABLED=0is mandatory and native syscalls are unavailable.- Module size or embedded targets favor TinyGo over gc wasm.
Working Example
A minimal WASI program that writes to stdout (no cgo, no syscall/js).
// cmd/hello/main.go
package main
import "fmt"
func main() {
fmt.Println("hello wasi")
}GOOS=wasip1 GOARCH=wasm go build -o hello.wasm ./cmd/hello
wasmtime hello.wasmWhat this demonstrates:
- Standard
fmtworks onwasip1via WASI imports linked by the Go wasm runtime. - Build tags can exclude cgo-only files with
//go:build !wasm. - Execution requires a wasm host (
wasmtime,wazero, Spin, etc.), not./hello.wasmdirectly.
Deep Dive
How It Works
- The gc toolchain compiles Go to WebAssembly with a target-specific syscall layer instead of libc cgo.
syscall/jsexposes JavaScript values and callbacks forGOOS=js; it is not used onwasip1.- WASI maps files, clocks, and randomness to host imports - closer to server workloads than DOM APIs.
- TinyGo is a separate compiler producing smaller wasm at the cost of language/runtime subset.
cgo vs wasm Interop
| Concern | Native cgo | Go wasm |
|---|---|---|
| C libraries | Link .so/.a | Not available; use wasm ports or hosts |
| Browser DOM | Not applicable | syscall/js callbacks |
| File I/O | libc / x/sys | WASI files or JS fs bridge |
| Binary size | Native ELF/Mach-O | .wasm module (gc wasm is larger than TinyGo) |
| Threads | OS threads | Limited wasm threading; check host support |
Entry Points to Learn Next
| Path | Build | Host | Deep dive |
|---|---|---|---|
| Browser | GOOS=js GOARCH=wasm | Browser + wasm_exec.js | syscall/js Go in the Browser |
| WASI server | GOOS=wasip1 GOARCH=wasm | wasmtime, wazero | Compiling with GOOS=wasip1 |
| TinyGo MCU | tinygo build -target=… | Board or wasm | TinyGo for microcontrollers |
Go Notes
//go:build !js && !wasm
package plugin
// cgo-only native implementation//go:build js || wasm
package plugin
// wasm-safe stub or pure Go implementationSplit packages so interop code does not pull import "C" into wasm builds.
Gotchas
- Expecting cgo drivers on wasm - Build fails or tags exclude packages. Fix: Pure-Go drivers and wasm-specific build files.
- Running gc wasm in browser without
wasm_exec.js- Loader errors. Fix: Follow Go wasm wiki wiring for your bundler. - Assuming full
neton js/wasm - Restricted by host sandboxes. Fix: Use fetch/WebSocket bridges documented forsyscall/js. - Huge wasm binaries from stdlib imports - Slow web loads. Fix: TinyGo or trim dependencies; see size guide in WebAssembly section.
- Mixing
wasip1andjstags - Different syscall sets. Fix: Separatemainpackages or explicit build tags per target. - Ignoring host capability matrix - WASI previews differ across runtimes. Fix: Test on wazero/wasmtime versions you ship.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Native cgo service | Need C SDK on Linux servers | Target is browser or wasm-only host |
syscall/js bridge | DOM and browser APIs required | Headless WASI batch jobs |
| TinyGo wasm | Size and MCU constraints dominate | You need full generics/runtime features |
| Rust/C++ wasm module | Team already ships wasm there | Go-only shop wants one language |
| RPC to native sidecar | Heavy C libs stay on Linux | Wasm sandbox is mandatory |
FAQs
Is cgo available on GOARCH=wasm?
No - WebAssembly builds are pure Go with target-specific syscalls.
Plan interop without import "C".
What is the difference between js and wasip1?
js targets browsers with syscall/js.
wasip1 targets WASI runtimes with POSIX-like imports, not the DOM.
Where is the full WebAssembly section?
See WebAssembly & TinyGo in this site - section order 38 in the Go guide manifest.
Start with Go on WebAssembly.
Does Go 1.26 improve wasm?
Recent releases improved wasm runtime and memory behavior.
Read Go 1.26 wasm runtime improvements for specifics pinned to this guide.
When should I pick TinyGo over gc wasm?
Microcontrollers, sub-megabyte modules, and embedded wasm.
Use gc wasm when you need full stdlib compatibility and desktop-class hosts.
Can wazero run my wasip1 binary?
Yes - wazero is a pure-Go wasm runtime suitable for embedding.
See WASI host runtimes.
How do I call JavaScript from Go in the browser?
Use syscall/js function values and callbacks.
Keep boundaries narrow to avoid leaking goroutines across JS events.
Are files and env vars available on wasip1?
WASI defines filesystem and environment imports; hosts map them to real or virtual resources.
Capabilities vary by runtime configuration.
Can I share code between linux amd64 and wasm?
Yes - business logic in pure Go packages with tagged platform entrypoints.
Keep cgo isolated behind //go:build cgo && !wasm.
Is wasm a replacement for all cgo?
No - native OS integration on Linux/macOS/Windows still needs cgo or x/sys.
Wasm is an additional deployment surface.
How big are Go wasm modules?
Gc wasm binaries are often tens of MB uncompressed before gzip.
TinyGo targets orders-of-magnitude smaller when language subset is acceptable.
What hosts does this guide reference?
wasmtime, wazero, and Spin appear in the dedicated section.
Verify versions at build per manifest pins for tinygo and wazero.
Related
- Go on WebAssembly: Browser, WASI, and Embedded Paths - Section entry explainer
- WebAssembly & TinyGo Basics - Hands-on wasm tour
- syscall/js: Go in the Browser - DOM and JS interop
- syscall & Pure-Go Alternatives to CGO - Native non-cgo options
- Interop Decision Guide: CGO vs RPC vs Rewrite - Pick wasm vs native
Stack versions: This page was written for Go 1.26.x (Green Tea GC default, go fix modernizers - verify patch at build), chi (latest - verify at build), gin (latest - verify at build), echo (latest - verify at build), google.golang.org/grpc (latest - verify at build), sigs.k8s.io/controller-runtime (latest - verify at build), kubebuilder (latest - verify at build), tinygo (latest - verify board targets at build), wazero (latest - verify at build), and golangci-lint (latest - verify linter set at build).