Step-by-Step Guide to Installing GHC-JS (Haskell JavaScript FFI) + Example
Table of Contents
Introduction
In this blog, I’ll walk you through installing and using the JavaScript Foreign Function Interface (FFI) in Haskell with GHC. This setup lets talk to Javascript from Haskell and vice versa, making it a powerful tool for web development or integrating Haskell with JavaScript-based platforms. Most resources I found online were outdated, so I’ve created this up-to-date guide based on my own experience to help you get started.
Prerequisites
I’m using a Linux machine (Ubuntu 22.04), but these instructions should work on other platforms like macOS or Windows with minor adjustments. You’ll need Node.js installed. If you don’t have it, I recommend using nvm to install the latest LTS version:
$ nvm install --lts
Verify the installation:
$ node --version
# Expected output: v20.12.2 (or similar LTS version)
Install GHC
The easiest way to install GHC and its tools is with GHCup. Run this command to install it:
$ curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh
After installation, you can use the Text User Interface (TUI) to manage GHC versions:
$ ghcup tui
This opens an interface showing available GHC, Cabal, and Stack versions. However, these standard GHC builds don’t include the JavaScript backend—we’ll enable that next.
Add Cross Channel
To enable the JavaScript backend, we need to add the cross release channel in GHCup. Release channels provide access to specialized GHC builds, including those for cross-compilation targets like JavaScript and wasm. Add it with:
$ ghcup config add-release-channel cross
Now, running ghcup tui
will show additional GHC versions, including those with JavaScript support. See the GHCup documentation for more details.

Install Emscripten
The JavaScript backend requires Emscripten, a toolchain for compiling to JavaScript and WebAssembly. The latest Emscripten version caused issues in my tests, so I recommend version 3.1.57, which works reliably with GHC’s JavaScript backend. Install it like this:
git clone https://github.com/emscripten-core/emsdk.git
$ cd emsdk
$ ./emsdk install 3.1.57
$ ./emsdk activate 3.1.57
$ source ./emsdk_env.sh
The source
command sets up environment variables for Emscripten. Verify it’s working with emcc --version
.

Set Alias for GHCJS
After installing the GHC JavaScript backend, set it as the default GHC version:
$ emconfigure ghcup install ghc javascript-unknown-ghcjs-9.10.0.20240413
$ ghcup set ghc javascript-unknown-ghcjs-9.10.0.20240413

For convenience, add an alias to your shell configuration file (e.g., .bashrc
or .zshrc
):
$ alias ghcjs=javascript-unknown-ghcjs-ghc
Run
source ~/.bashrc
to apply the alias.
Example
