Skip to main content
Miso supports hot reload through WASM browser mode — a feature of the GHC WASM backend that runs GHCi inside the browser tab. When combined with ghciwatch, file changes trigger immediate recompilation and the updated application is reflected in the browser without a full page reload.
Hot reload in miso uses the WASM backend only. The GHCJS (JavaScript) backend does not support GHCi browser mode.

Requirements

  • GHC WASM backend — acquired via nix develop .#wasm from miso’s flake (see Nix)
  • ghciwatch — included in the WASM dev shell
  • miso-sampler — the template repository with ready-made build scripts

Setup

1

Install Nix and enable flakes

curl -L https://nixos.org/nix/install | sh
echo 'experimental-features = nix-command flakes' >> ~/.config/nix/config.nix
2

Clone the miso-sampler template

git clone https://github.com/haskell-miso/miso-sampler
cd miso-sampler
miso-sampler is the official template repository. It includes a Makefile with build targets for WASM, GHCJS, and GHC.
3

Enter the WASM dev shell

nix develop .#wasm
This shell provides the GHC WASM 9.12.2 toolchain, ghciwatch, bun, http-server, and cabal-install.
4

Start hot reload

The WASM shell exposes a repl-watch function:
repl-watch
This runs:
ghciwatch \
  --after-reload-ghci :main \
  --watch . \
  --debounce 50ms \
  --command 'wasm32-wasi-cabal repl app -finteractive \
    --repl-options="-fghci-browser -fghci-browser-port=8080"'
ghciwatch watches all files in the current directory. When a Haskell source file changes, it reloads GHCi and calls :main, which re-runs the miso application in the browser.
5

Open the browser

Navigate to http://localhost:8080. Changes to your .hs files will be reflected automatically after each save.

How it works

The GHC WASM backend includes a special GHCi mode (-fghci-browser) that compiles Haskell to WASM and executes it directly inside a browser tab over a WebSocket connection. Instead of a full compile-link-bundle cycle, GHCi loads only changed modules. ghciwatch coordinates the outer loop:
  1. Watches the filesystem for changes
  2. Sends a reload command to the running GHCi session
  3. Runs :main after each successful reload
The --debounce 50ms flag prevents rapid successive saves from triggering multiple reloads.

Shell functions

The WASM dev shell defines several convenience functions:
# Full WASM build (no hot reload)
build

# Standard GHCi session in the browser (single session, no file watching)
repl

# GHCi + ghciwatch (hot reload)
repl-watch

# Clean build artifacts
clean

# Update package index
update

Production builds

Hot reload is a development-only workflow. For production, build the full WASM bundle:
build
make serve
The -fghci-browser flag must not be used in production builds. It opens a WebSocket control channel that is only appropriate during development.

Miso.Reload functions

The Miso.Reload module exports two functions for use with WASM browser mode:
import Miso.Reload

-- | Clears <body> and <head> on each reload, then re-draws the app.
-- All Component state is reset on each reload.
reload
  :: Eq model
  => Events
  -> App model action
  -> IO ()

-- | Live reload — persists all Component model state between GHCi reloads.
-- Use this when you are changing view/update but NOT the model schema.
-- Warning: changing model fields (add/remove/type change) will likely segfault.
live
  :: Eq model
  => Events
  -> App model action
  -> IO ()
Choose based on what you’re changing:
UseWhen
liveIterating on view or update logic, model schema is stable
reloadChanging model type (adding/removing/changing fields)
The miso-sampler uses live by default:
main :: IO ()
#ifdef INTERACTIVE
main = live defaultEvents app
#else
main = startApp defaultEvents app
#endif
The INTERACTIVE CPP flag is set during hot reload builds.

Further reading

Build docs developers (and LLMs) love