Installation
March 4, 2026 ยท View on GitHub
Compilation
When done developing, we can compile to Web Assembly or JavaScript for distribution. This is done by acquiring a GHC that supports WebAssembly or JavaScript. We recommend acquiring these backends using GHCUp or Nix.
Web Assembly
Tip
The Haskell miso team currently recommends using the WASM backend as the default backend for compilation.
Using GHCup you should be able to acquire the GHC WASM compiler.
For instructions on how to add a third-party channel with GHCup, please see their official README.md
$ nix shell 'gitlab:haskell-wasm/ghc-wasm-meta?host=gitlab.haskell.org'
Note
This will put wasm32-wasi-cabal in your $PATH, along with wasm32-wasi-ghc. Since the WASM backend is relatively new, the ecosystem is not entirely patched to support it. Therefore, we will need to use patched packages from time to time.
Tip
Instead of using a nix shell, it's possible to install the GHC WASM Flake into your environment so it will always be present on $PATH
$ nix profile install 'gitlab:haskell-wasm/ghc-wasm-meta?host=gitlab.haskell.org'
Update your cabal.project to the following
cabal.project
packages:
.
with-compiler:
wasm32-wasi-ghc
with-hc-pkg:
wasm32-wasi-ghc-pkg
source-repository-package
type: git
location: https://github.com/dmjio/miso
branch: master
if arch(wasm32)
-- Required for TemplateHaskell. When using wasm32-wasi-cabal from
-- ghc-wasm-meta, this is superseded by the global cabal.config.
shared: True
Call wasm32-wasi-cabal build --allow-newer and a WASM payload should be created in dist-newstyle/ directory.
$ wasm32-wasi-cabal update
$ wasm32-wasi-cabal build --allow-newer
Configuration is affected by the following files:
- cabal.project
Resolving dependencies...
Build profile: -w ghc-9.12.2.20250327 -O1
In order, the following will be built (use -v for more details):
- app-0.1.0.0 (exe:app) (configuration changed)
Configuring executable 'app' for app-0.1.0.0...
Preprocessing executable 'app' for app-0.1.0.0...
Building executable 'app' for app-0.1.0.0...
[1 of 1] Compiling Main ( Main.hs, dist-newstyle/build/wasm32-wasi/ghc-9.12.2.20250327/app-0.1.0.0/x/app/build/app/app-tmp/Main.o )
[2 of 2] Linking dist-newstyle/build/wasm32-wasi/ghc-9.12.2.20250327/app-0.1.0.0/x/app/build/app/app.wasm
You have now successfully compiled a Haskell miso application to WebAssembly ๐ฅ
But, we're not done yet. In order to view this in the browser there are still a few more steps. We need to add some additional files that emulate the WASI interface in the browser (A browser WASI shim).
Note
The GHC WASM backend can execute any Haskell program in a WASI-compliant runtime (e.g. wasmtime) See the official documentation for more information.
To start, we recommend creating an app.wasmexe folder to store the additional artifacts required.
Tip
We recommend using an up-to-date node version (currently tested with v24.2.0) to ensure post-link.mjs works properly.
# Creates the directory for hosting
$ mkdir -v app.wasmexe
mkdir: created directory 'app.wasmexe'
# This command produces `ghc_wasm_jsffi.js`, which ensures our FFI works properly.
$ $(wasm32-wasi-ghc --print-libdir)/post-link.mjs \
--input $(wasm32-wasi-cabal list-bin app --allow-newer) \
--output app.wasmexe/ghc_wasm_jsffi.js
# This copies the `app.wasm` payload into `app.wasmexe`
$ cp -v $(wasm32-wasi-cabal list-bin app --allow-newer) app.wasmexe
Configuration is affected by the following files:
- cabal.project
'/home/dmjio/Desktop/miso/sample-app/dist-newstyle/build/wasm32-wasi/ghc-9.12.2.20250327/app-0.1.0.0/x/app/build/app/app.wasm' -> 'app.wasmexe'
Note
Along with the above ghc_wasm_jsffi.js and app.wasm artifacts, we also need to include an index.html and an index.js for loading the WASM payload into the browser.
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Sample miso WASM counter app</title>
</head>
<body>
<script src="index.js" type="module"></script>
</body>
</html>
index.js
import { WASI, OpenFile, File, ConsoleStdout } from "https://cdn.jsdelivr.net/npm/@bjorn3/browser_wasi_shim@0.3.0/dist/index.js";
import ghc_wasm_jsffi from "./ghc_wasm_jsffi.js";
const args = [];
const env = ["GHCRTS=-H64m"];
const fds = [
new OpenFile(new File([])), // stdin
ConsoleStdout.lineBuffered((msg) => console.log(`[WASI stdout] ''${msg}`)),
ConsoleStdout.lineBuffered((msg) => console.warn(`[WASI stderr] ''${msg}`)),
];
const options = { debug: false };
const wasi = new WASI(args, env, fds, options);
const instance_exports = {};
const { instance } = await WebAssembly.instantiateStreaming(fetch("app.wasm"), {
wasi_snapshot_preview1: wasi.wasiImport,
ghc_wasm_jsffi: ghc_wasm_jsffi(instance_exports),
});
Object.assign(instance_exports, instance.exports);
wasi.initialize(instance);
await instance.exports.hs_start(globalThis.example);
The app.wasmexe folder will now look like:
โฏ ls app.wasmexe
๏
app.wasm
๎ ghc_wasm_jsffi.js
๏ป index.html
๎ index.js
Now you can host and view the app.wasm payload in a web browser.
$ http-server app.wasmexe
Tip
You can inspect the WASM payload in the Sources tab of your browser by right-clicking and then clicking Inspect.
JavaScript
Using GHCup you should be able to acquire the latest GHC JS-backend compiler.
Tip
For Nix users it is possible to acquire the latest JS backend (that miso uses) via Nix.
Use cachix to ensure you're not building dependencies unnecessarily cachix use haskell-miso-cachix
nix-shell -p pkgs.pkgsCross.ghcjs.haskell.packages.ghc9122.ghc -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/65f179f903e8bbeff3215cd613bdc570940c0eab.tar.gz
Note
This will put javascript-unknown-ghcjs-ghc in your $PATH, along with javascript-unknown-ghcjs-ghc-pkg. You might also need to specify in your cabal.project file that you are using the JS backend.
Tip
Alternatively, if you'd like to install the compiler into your global environment (so you don't need to develop inside a bash shell) you can use the following command.
nix-env -iA pkgs.pkgsCross.ghcjs.haskell.packages.ghc9122.ghc -f https://github.com/NixOS/nixpkgs/archive/65f179f903e8bbeff3215cd613bdc570940c0eab.tar.gz
cabal.project
packages:
.
source-repository-package
type: git
location: https://github.com/dmjio/miso
branch: master
with-compiler:
javascript-unknown-ghcjs-ghc
with-hc-pkg:
javascript-unknown-ghcjs-ghc-pkg
Note
cabal will use the ghc specified above in with-compiler
$ cabal update && cabal build --allow-newer
Configuring executable 'app' for app-0.1.0.0...
Preprocessing executable 'app' for app-0.1.0.0...
Building executable 'app' for app-0.1.0.0...
[1 of 1] Compiling Main ( Main.hs, dist-newstyle/build/javascript-ghcjs/ghc-9.12.2/app-0.1.0.0/x/app/build/app/app-tmp/Main.o )
[2 of 2] Linking dist-newstyle/build/javascript-ghcjs/ghc-9.12.2/app-0.1.0.0/x/app/build/app/app.jsexe
Tip
To view the JavaScript in your browser, you can use cabal list-bin and http-server
$ http-server $(cabal list-bin app --allow-newer).jsexe
Configuration is affected by the following files:
- cabal.project
Starting up http-server, serving /home/dmjio/Desktop/miso/sample-app/dist-newstyle/build/javascript-ghcjs/ghc-9.12.2/app-0.1.0.0/x/app/build/app/app.jsexe
http-server version: 14.1.1
http-server settings:
CORS: disabled
Cache: 3600 seconds
Connection Timeout: 120 seconds
Directory Listings: visible
AutoIndex: visible
Serve GZIP Files: false
Serve Brotli Files: false
Default File Extension: none
Available on:
http://127.0.0.1:8080
http://192.168.1.114:8080
Hit CTRL-C to stop the server