ghc-wasm-bindgen: JavaScript/TypeScript bindings for GHC's WASM backend

[ development, javascript, library, mit, program, web, webassembly ] [ Propose Tags ] [ Report a vulnerability ]

Generate JavaScript and TypeScript bindings for a pure Haskell module compiled with GHC's WebAssembly backend. Exported functions are exposed as typed JavaScript functions, and exported data types as TypeScript types with constructor functions.

See the README for supported types and usage.


[Skip to Readme]

Modules

[Last Documentation]

  • GhcWasmBindgen
    • GhcWasmBindgen.EmitHaskell
    • GhcWasmBindgen.EmitJavaScript
    • GhcWasmBindgen.Inspect
    • GhcWasmBindgen.Model

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees

Candidates

  • No Candidates
Versions [RSS] 0.1.0.0, 0.2.0.0, 0.3.0.0
Dependencies base (>=4.20 && <4.21), bytestring (>=0.11 && <0.13), containers (>=0.6 && <0.8), directory (>=1.3.8 && <1.4), file-embed (>=0.0.15 && <0.1), filepath (>=1.4.100 && <1.6), ghc (>=9.10 && <9.11), ghc-paths (>=0.1.0.12 && <0.2), ghc-wasm-bindgen, process (>=1.6.17 && <1.7), text (>=2.0 && <2.2) [details]
Tested with ghc ==9.10.3
License MIT
Copyright 2026 Joaquín Rossi
Author Joaquín Rossi
Maintainer joaquin@joaquinrossi.net
Uploaded by joaquinrossi at 2026-11-10T20:38:46Z
Category Web, WebAssembly, JavaScript, Development
Home page https://github.com/joaquin-rossi/ghc-wasm-bindgen
Bug tracker https://github.com/joaquin-rossi/ghc-wasm-bindgen/issues
Source repo head: git clone https://github.com/joaquin-rossi/ghc-wasm-bindgen.git
Distributions
Executables ghc-wasm-bindgen
Downloads 7 total (7 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs not available [build log]
All reported builds failed as of 2026-11-10 [all 2 reports]

Readme for ghc-wasm-bindgen-0.3.0.0

[back to package description]

ghc-wasm-bindgen

Generate JavaScript/TypeScript bindings for a pure Haskell module compiled with GHC's WASM backend.

module ShapeWasm (Shape (..), area, scale, largest) where
import Shape
import { area, Circle, largest, Rect, scale } from "shape";

area(Rect({ width: 3, height: 4 }));  // 12
scale(2, Circle(1));                  // { $tag: "Circle", contents: 2 }
largest([]);                          // null
area(Circle("x"));                    // TypeScript error

See example/ for a complete project.

Types

Haskell TypeScript
Double, Float number
Int, Word, Int8…Int64, Word8…Word64 number
Integer, Natural bigint
Bool boolean
Char, String, Text string
Day string (YYYY-MM-DD)
UTCTime Date
Maybe a a | null
Either e a { Left: e } | { Right: a }
(a, b) [a, b]
[a], Vector a a[]
NonEmpty a [a, ...a[]]
Set a, HashSet a, IntSet Set<a>
Map k v, HashMap k v, IntMap v Map<k, v>

Your own data and newtype declarations:

Constructors TypeScript
with record fields { $tag: "C"; field: … }
positional { $tag: "C"; contents: … }
all without fields (enum) "A" | "B"

Notes:

  • Every type the API uses must be exported with all its constructors.
  • Not supported yet: IO, polymorphic functions, functions as arguments, operators, types with parameters and GADTs.

Errors

  • An argument that does not fit its type (an out-of-range integer, a Char that is not one code point, an unknown constructor, …) throws a TypeError before reaching Haskell.
  • An exception in Haskell throws a HaskellError (exported by the bindings).

Usage

build: cabal projects

From the cabal project that contains the module:

ghc-wasm-bindgen build src/ShapeWasm.hs --name shape --out pkg/src

The module must be exposed by a library, in the same package as the code it wraps or in one that depends on it. This writes shape.js, shape.d.ts, shape.wasm and the JS runtime to pkg/src/, and nothing else, so the directory around it can be maintained by hand, even in another repository.

To publish it as an npm package, write its package.json yourself, for example:

{
    "name": "shape",
    "version": "0.1.0",
    "type": "module",
    "exports": {
        ".": { "types": "./src/shape.d.ts", "default": "./src/shape.js" }
    }
}

generate: plain source files

ghc-wasm-bindgen generate api/ShapeWasm.hs -i lib --name shape --hs-out gen --js-out out

This writes the Haskell bindings and runtime to gen/, and the JS bindings and runtime to out/. Compiling is up to you:

wasm32-wasi-cabal install --lib aeson text --package-env .   # once
wasm32-wasi-ghc -package ghc-experimental -igen -ilib -iapi gen/ShapeWasmBindings.hs \
    -no-hs-main -optl-mexec-model=reactor -outputdir build -o shape.wasm
"$(wasm32-wasi-ghc --print-libdir)"/post-link.mjs -i shape.wasm -o out/ghc_wasm_jsffi.js
wasm-opt -Oz shape.wasm -o out/shape.wasm

shape.wasm must end up next to shape.js.

Loading

shape.js loads shape.wasm from its own directory with a top-level await, so importing it is enough: the functions are ready to call. If the .wasm cannot be loaded, the import fails.

This needs ES2022 modules (top-level await): current browsers, Node 20.16+ or 22.3+, and bundlers targeting ES2022 or later. The .wasm is referenced as new URL("./shape.wasm", import.meta.url):

  • webpack 5, Vite and Parcel copy it to the output on their own. With Vite, exclude the package from dependency pre-bundling (optimizeDeps: { exclude: ["shape"] }), which would break that reference in development.
  • esbuild leaves the reference as is: copy shape.wasm next to the bundle (the example does this).

Installation

cabal install ghc-wasm-bindgen

Requirements:

  • GHC 9.10 and cabal.

  • The WASM toolchain from ghc-wasm-meta on PATH (tested with GHC 9.14):

    curl https://gitlab.haskell.org/haskell-wasm/ghc-wasm-meta/-/raw/master/bootstrap.sh | FLAVOUR=9.14 sh