Skip to main content
Miso supports native mobile application development via miso-lynx, a separate repository that targets LynxJS as its rendering backend.
miso-lynx is maintained in a separate repository from the miso core: github.com/haskell-miso/miso-lynx. The miso core package does not need to change to support mobile targets.

Supported platforms

iOS

Native iOS applications via LynxJS.

Android

Native Android applications via LynxJS.

HarmonyOS

Huawei HarmonyOS device support.

What is LynxJS?

LynxJS is a cross-platform rendering engine that allows JavaScript/Haskell logic to drive native UI components on iOS, Android, and HarmonyOS. Instead of a browser DOM, LynxJS provides a native rendering pipeline that maps declarative view trees to platform widgets. Miso’s virtual DOM abstraction maps cleanly onto LynxJS’s component model: the same view function and update loop you write for the web can be reused for mobile with a different renderer underneath.

Architecture

Miso’s rendering is decoupled from any specific DOM implementation through a renderer abstraction. The relevant pieces are:
  • SomeComponent — a type-erased component wrapper used by the runtime
  • Renderer — the piece that translates miso’s virtual DOM patch operations into actual DOM (or native UI) mutations
  • GHC JS backend — miso-lynx uses the GHC JavaScript backend (javascript-unknown-ghcjs-ghc) to compile Haskell to JavaScript, which is then loaded by the LynxJS runtime on the device
The native dev shell in miso’s flake targets this configuration:
# GHCJS shell for building iOS / Android apps targeting LynxJS.org.
native =
  pkgs.mkShell {
    name = "The miso-native ${system} GHC JS 9.12.2 shell";
    packages = with pkgs; [
      pkgsCross.ghcjs.haskell.packages.ghcNative.ghc
      gnumake
      http-server
      cabal-install
      emscripten
      tailwindcss_4
    ];
  };
Enter this shell with:
nix develop .#native

Building

Inside the native dev shell, build with the GHC JS backend:
cabal build \
  --with-compiler=javascript-unknown-ghcjs-ghc \
  --with-hc-pkg=javascript-unknown-ghcjs-ghc-pkg
The resulting JavaScript bundle is then packaged and loaded by the LynxJS runtime on the target device. See the miso-lynx repository for device-specific packaging instructions.

Writing mobile views

Miso-lynx apps are written in the same style as web miso apps. The component function, Effect monad, and view function work identically. Only the set of available elements and properties differs — instead of HTML elements you use LynxJS component types.
-- A miso-lynx view looks structurally identical to a web view.
viewModel :: Model -> View Model Action
viewModel model =
  -- LynxJS element constructors replace Miso.Html constructors
  view_ [] [ text_ [] [ text (ms (counter model)) ] ]
Check the miso-lynx repository for the current status of supported elements, installation instructions, and example applications.

Relationship to miso core

miso (core)miso-lynx
TargetsWeb (WASM, GHCJS, GHC)iOS, Android, HarmonyOS
RendererBrowser DOMLynxJS native renderer
Repositorydmjio/misohaskell-miso/miso-lynx
CompilerGHC WASM or GHCJSGHC JS backend (GHCJS)
ArchitectureIsomorphic (client + server)Mobile-native
The programming model is intentionally shared: the Effect, component, and virtual DOM types come from miso core. miso-lynx only replaces the renderer and element vocabulary.

Build docs developers (and LLMs) love