A Haskell library for building web and mobile applications
miso is a component-oriented Haskell library for building web and mobile applications. It is pure by default; side effects are introduced explicitly through the Effect type. The library is designed in the spirit of Elm and draws inspiration from React including Component, Fragment and Props.
- Virtual DOM with a recursive diffing and patching algorithm
- Attribute and property normalization, event delegation, and event batching
- SVG, 2D Canvas, and WebGL (via three.js) rendering
- Fetch, Server-Sent Events, and WebSocket support
- Type-safe client-side routing
- An extensible subscription-based subsystem for long-running effects
- Reactive extensions for fine-grained reactivity
It makes heavy use of the GHC JavaScript FFI and maintains minimal dependencies, and can be considered a shallow embedded domain-specific language for modern web programming. Compilation targets include JavaScript and WebAssembly via GHC. Hot reload is provided through WASM browser mode integrated with ghciwatch.
Tip
See the Haskell miso organization on GitHub for the full ecosystem of packages and examples π
- Playground
- Quick Start (Nix)
- Manual Setup (GHCup / Cabal)
- Hot Reload
- Installation
- Haddocks
- Wiki
- Architecture
- Examples
- HTTP
- Testing
- Native
- Benchmarks
- Nix
- Community
- Maintainers
- Commercial
- Contributing
- Contributors
- Partnerships
- Backers
- Organizations
- History
- License
An interactive playground is available at try.haskell-miso.org. It allows editing and running applications directly in the browser without any local toolchain setup, and is useful for experimentation and sharing minimal reproducible examples.
Tip
The miso-sampler template repository includes a counter application with build scripts for WebAssembly, JavaScript, and native GHC targets.
The following requires Nix Flakes. See also Binary cache to avoid rebuilding dependencies.
# Install nix
curl -L https://nixos.org/nix/install | sh
# Enable flakes
echo 'experimental-features = nix-command flakes' >> ~/.config/nix/nix.conf
# Clone, build and serve
git clone https://github.com/haskell-miso/miso-sampler && cd miso-sampler
nix develop .#wasm --command bash -c 'make && make serve'To develop applications without Nix, acquire GHC and cabal via GHCup.
A minimal application requires three files:
cabal.projectapp.cabalMain.hs
packages:
.
source-repository-package
type: git
location: https://github.com/dmjio/miso
branch: masterNote
Pinning to a specific tag: or commit: rather than branch: master is recommended for reproducible builds.
Using cabal-version: 2.2 or later enables common stanzas, which allow a single .cabal file to target both the WASM and JS backends.
cabal-version: 2.2
name: app
version: 0.1.0.0
synopsis: Sample miso app
category: Web
common options
if arch(wasm32)
ghc-options:
-no-hs-main
-optl-mexec-model=reactor
"-optl-Wl,--export=hs_start"
cpp-options:
-DWASM
if arch(javascript)
ld-options:
-sEXPORTED_RUNTIME_METHODS=HEAP8
executable app
import:
options
main-is:
Main.hs
build-depends:
base, miso
default-language:
Haskell2010A counter application demonstrating the Model-View-Update pattern:
----------------------------------------------------------------------------
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE CPP #-}
----------------------------------------------------------------------------
module Main where
----------------------------------------------------------------------------
import Miso
import qualified Miso.Html as H
import Miso.Lens
----------------------------------------------------------------------------
-- | Sum type for App events
data Action
= AddOne
| SubtractOne
| SayHelloWorld
deriving (Show, Eq)
----------------------------------------------------------------------------
-- | Entry point for a miso application
main :: IO ()
main = startApp defaultEvents app
----------------------------------------------------------------------------
-- | WASM export, required when compiling w/ the WASM backend.
#ifdef WASM
foreign export javascript "hs_start" main :: IO ()
#endif
----------------------------------------------------------------------------
-- | `vcomp` takes as arguments the initial model, update function, view function
app :: App Int Action
app = vcomp 0 updateModel viewModel
----------------------------------------------------------------------------
-- | Updates model, optionally introduces side effects
updateModel :: Action -> Effect parent props Int Action
updateModel = \case
AddOne -> this += 1
SubtractOne -> this -= 1
SayHelloWorld -> io_ $ do
alert "Hello World"
consoleLog "Hello World"
----------------------------------------------------------------------------
-- | Constructs a virtual DOM from a model
viewModel :: () -> Int -> View Int Action
viewModel _props x = vfrag
[ H.button_ [ H.onClick AddOne ] [ text "+" ]
, text (ms x)
, H.button_ [ H.onClick SubtractOne ] [ text "-" ]
, H.br_ []
, H.button_ [ H.onClick SayHelloWorld ] [ text "Alert Hello World!" ]
]
----------------------------------------------------------------------------Hot reload is supported via WASM browser mode and ghciwatch. This provides incremental recompilation with automatic browser refresh on file changes. See the miso-sampler browser mode documentation for setup instructions.
See Installation for platform-specific installation instructions.
Official API reference. See also the Miso module for a guided entry point into the library.
| Platform | URL |
|---|---|
| GHCJS | Link |
| GHC | Link |
See the DeepWiki entry for an AI-assisted exploration of the source code.
miso follows the Model-View-Update (MVU) pattern. A Component is parameterized by a model type and an action type. The update function maps actions to Effect values β a monad over the Reader/Writer/State stack β which can both modify the model and schedule IO operations. Long-running effects are expressed as Subscriptions that push actions into the component via a Sink.
For (client/server) applications, the recommended layout is a single .cabal file with separate executable stanzas conditioned on the compiler target. An example of this structure is the haskell-miso.org source.
Tip
For a worked example of a Nix-based client/server deployment, see the nix scripts for haskell-miso.org.
Examples are hosted under the haskell-miso GitHub organization. Each repository contains its own build instructions. The recommended approach is to build via nix.
Tip
Use cachix to avoid rebuilding shared dependencies: cachix use haskell-miso-cachix
| Name | Description | Source | Demo | Author |
|---|---|---|---|---|
| TodoMVC | TodoMVC reference implementation | Source | Demo | @dmjio |
| 2048 | Clone of the 2048 sliding-tile game | Source | Demo | @ptigwe |
| Flatris | Tetris variant | Source | Demo | @ptigwe |
| Plane | Flappy-bird-style game | Source | Demo | @Lermex |
| Snake | Classic Snake game | Source | Demo | @lbonn |
| SVG | SVG rendering | Source | Demo | @dmjio |
| Fetch | HTTP API interaction via Fetch | Source | Demo | @dmjio |
| File Reader | FileReader API | Source | Demo | @dmjio |
| Mario | Physics-based platformer | Source | Demo | @dmjio |
| WebSocket | WebSocket communication | Source | Demo | @dmjio |
| Router | Client-side routing | Source | Demo | @dmjio |
| Canvas 2D | 2D Canvas rendering | Source | Demo | @dmjio |
| MathML | MathML rendering | Source | Demo | @dmjio |
| Simple | Counter (minimal example) | Source | Demo | @dmjio |
| SSE | Server-Sent Events | Source | Demo | @dmjio |
| Three.js | 3D rendering via Three.js | Source | Demo | @juliendehos |
| Space Invaders | Space Invaders clone | Source | Demo | @juliendehos |
| Audio | Audio playback | Source | Demo | @juliendehos |
| Video | Video playback | Source | Demo | @juliendehos |
| WebVR | WebVR via A-Frame | Source | Demo | @dmjio |
| Reactivity | Fine-grained reactive updates | Source | Demo | @dmjio |
| Chess | Chess game | Source | Demo | @dmjio |
Two approaches are supported:
-
For simple JSON-based APIs, use the Fetch module directly.
-
For more complex cases, define a Servant API and derive client functions via servant-miso-client.
The Fetch example (Demo) demonstrates the required setup. Add the following to
cabal.projectto useservant-miso-client:source-repository-package type: git location: https://github.com/haskell-miso/servant-miso-client tag: master
The test suite spans three layers:
- Unit tests β the TypeScript runtime (virtual DOM, diffing, event delegation) is tested with bun, covering the core
diffengine and supporting utilities. - Integration tests β Haskell internals are exercised via a WASM test suite that runs the runtime in a headless browser environment, verifying component lifecycle, subscriptions, and state transitions.
- End-to-end tests β selected applications such as TodoMVC are tested end-to-end against a live browser to validate full-stack rendering and event handling.
A full coverage report for the TypeScript layer is available at coverage.haskell-miso.org.
Note
To run the TypeScript tests, install bun first.
$ curl -fsSL https://bun.sh/install | bashor
$ nix-env -iA bun -f '<nixpkgs>'and
$ bun install && bun run testiOS and Android applications are supported via LynxJS. See the miso-lynx repository for details.
According to benchmarks, miso performs competitively relative to other frameworks.
Nix provides a reproducible environment for building, configuring, and deploying applications. The haskell-miso.org source serves as a reference for this workflow.
By default, miso uses a pinned version of nixpkgs known as pkgs.
Note
miso also maintains a legacyPkgs nixpkgs pin for tools such as nixops and for builds using the original GHCJS 8.6 backend.
Linux and macOS users can use a binary cache to avoid rebuilding dependencies. Follow the setup instructions on cachix.
$ cachix use haskell-miso-cachixFor CI pipelines using GitHub Actions:
- name: Install cachix
uses: cachix/cachix-action@v16
with:
name: haskell-miso-cachixSince its launch, miso has been deployed across a range of domains, including quantitative finance, network security, defense research, academia, SaaS, the public sector, and non-profit organizations. The largest known deployment consisted of approximately 200,000 LOC serving over 10,000 users.
Contributions are welcome. Open an issue or submit a pull request.
See CONTRIBUTING for guidelines.
Note
This project exists thanks to all the people who contribute.
For inquiries regarding feature sponsorship or corporate partnerships, contact support@haskell-miso.org.
Become a financial contributor to help sustain the project.
Support this project with your organization. Your logo will appear here with a link to your website.
miso is a portmanteau of micro and isomorphic.
miso was initiated in 2016 as a research project exploring two directions:
- Expressing the Elm architecture in GHCJS as an embedded domain-specific language
- Implementing reconciliation and isomorphic rendering techniques from the JavaScript ecosystem (Reconciliation, isomorphic JavaScript) within a purely functional setting
The project addresses the JavaScript problem in Haskell by providing component abstractions and rendering primitives familiar to practitioners of frameworks such as React and Vue.js. The library has since expanded to include multiple rendering backends and native mobile support for iOS, Android, and HarmonyOS via LynxJS.
BSD3 Β© dmjio