A tasty Haskell mobile framework π π π±
Miso Lynx π π is a mobile framework that uses miso and LynxJS to facilitate drawing to native iOS UIView, Android View, and for interacting with APIs on the device. The Haskell miso project is excited to be an open-source technology partner with innovative companies like ByteDance π¨π³ π¦Ύ , creators of LynxJS, to advance native mobile app development in the functional programming space.
Since 2017, miso
has sought ways to run on mobile devices that take advantage of native drawing facilities. Flutter, SwiftUI produce stunning, performant user experiences but are not necessarily designed for integration with open standards, are effectively only usable with a specific IDE / choice languages, and can lack in cross-platform capability (SwiftUI specifically). React Native improves upon this situation by allowing open web standards for development and cross-platform capability, but wasn't designed for easy integration with any web framework (rather for use specifically with react). React Native also can regress in performance relative to SwiftUI in certain scenarios.
Lynx addresses the aforementioned issues with a new architectural approach.
-
The Lynx engine uses two embedded JavaScript interpreters to selectively schedule / offload computation to free up the render thread. This avoids drawing lag as commonly seen with scroll events in react native applications.
-
Exposing a DOM API for rendering allows any JavaScript (or compile-to-JavaScript π) web framework to produce cross-platfom mobile applications. Seen here in
miso-lynx
. -
Lynx targets iOS, Android, Web by default, and has a roadmap that mentions Desktop UI as well (OSX, etc.)
-
Instant first-frame rendering - IFR
The flagship isomorphic (server side rendering) feature in
miso
can be repurposed as IFR inmiso-lynx
(one of the flagship features of lynx).
For framework implementors, this is a dream come true, and we hope miso-lynx
can be an ideal development environment for building Lynx applications with miso.
- React Summit
- Fireship
- Demo
- Preview
- Quick Start
- Setup
- Hot Reload
- Binary cache
- Maintainers
- Contributing
- License
As seen @ React Summit by @huxpro !
The Haskell miso portion is queued here.
See Fireship π₯ video

To run the example locally execute the following command
$ git clone git@github.com:haskell-miso/miso-lynx.git
$ http-server ./miso-lynx/examples
This will host the main.lynx.bundle
which can be loaded into the LynxExplorer
for interactive development.
Note
You will need to have the LynxExplorer installed which works with the iOS simulator. Please see the LynxJS getting started guide for installation.
Warning
miso-lynx
depends on the latest version of miso
(version 1.9
), this includes custom renderers (ala React Renderer) and Components as well.
Currently all event handling and drawing are performed on the main thread. Selectively scheduling Haskell code on the Lynx MTS / BTS is ongoing research.
This project is under heavy development and is considered experimental.
To start developing applications with miso-lynx
you will need to acquire GHC and cabal. This can be done via GHCup or Nix.
This file contains a simple miso-lynx
counter application.
-----------------------------------------------------------------------------
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-----------------------------------------------------------------------------
module Main where
-----------------------------------------------------------------------------
import Miso
import Miso.Lynx
-----------------------------------------------------------------------------
import Miso.Lens
import Miso.String
import qualified Miso.Style as CSS
-----------------------------------------------------------------------------
-- | Type synonym for an application model
newtype Model = Model { _value :: Int }
deriving (Show, Eq, ToMisoString)
-----------------------------------------------------------------------------
value :: Lens Model Int
value = lens _value $ \m v -> m { _value = v }
-----------------------------------------------------------------------------
data Action
= AddOne
| SubtractOne
| SayHelloWorld
| Tap Int
deriving (Show, Eq)
-----------------------------------------------------------------------------
-- | Entry point for a miso application
main :: IO ()
main = run $ lynx counterComponent
{ events = lynxEvents
, initialAction = Just SayHelloWorld
}
-----------------------------------------------------------------------------
counterComponent :: Component Model Action
counterComponent = component (Model 0) updateModel viewModel
-----------------------------------------------------------------------------
updateModel
:: Action
-> Effect Model Action
updateModel SayHelloWorld =
io_ (consoleLog "Hello World!")
updateModel AddOne =
value += 1
updateModel SubtractOne =
value -= 1
updateModel (Tap x) =
io_ $ consoleLog ("Tapped: " <> ms (show x))
-----------------------------------------------------------------------------
-- | Constructs a virtual DOM from a model
viewModel :: Model -> View Action
viewModel m = view_
[ CSS.style_
[ CSS.height "200px"
, CSS.display "flex"
, CSS.alignItems "center"
, CSS.justifyContent "center"
]
]
[ view_
[ onTap AddOne
, id_ "plus"
, CSS.style_
[ CSS.backgroundColor CSS.yellow
, CSS.width "100px"
, CSS.height "100px"
, CSS.margin "2px"
, CSS.display "flex"
, CSS.alignItems "center"
, CSS.justifyContent "center"
]
]
[ text_
[ CSS.style_
[ CSS.fontSize "48px"
]
]
[ "π"
]
]
, view_
[ CSS.style_
[ CSS.backgroundColor CSS.orange
, CSS.width "100px"
, CSS.height "100px"
, CSS.display "flex"
, CSS.alignItems "center"
, CSS.justifyContent "center"
]
]
[ text_
[ CSS.style_
[ CSS.fontSize "48px"
]
]
[ text $ ms (m ^. value)
]
]
, view_
[ onTap SubtractOne
, id_ "minus"
, CSS.style_
[ CSS.backgroundColor CSS.pink
, CSS.width "100px"
, CSS.height "100px"
, CSS.margin "2px"
, CSS.display "flex"
, CSS.alignItems "center"
, CSS.justifyContent "center"
]
]
[ text_
[ CSS.style_
[ CSS.fontSize "48px"
]
]
[ "π"
]
]
]
-----------------------------------------------------------------------------
Now that your project files are populated, development can begin.
This entails creating a LynxExplorer application with the jsaddle-warp hot-reload package. This will require using rspack and the BTS to access WebSockets via the lynx
object.
nix
users on a Linux or OSX distros can take advantage of a binary cache for faster builds. To use the binary cache follow the instructions on cachix.
Tip
We highly recommend nix users consume the cachix cache. cachix use haskell-miso-cachix
.
$ cachix use haskell-miso-cachix
Feel free to dive in! Open an issue or a submit Pull Request.
See CONTRIBUTING for more info.
BSD3 Β© dmjio