:ramen: :boom: miso-reactive
January 30, 2026 ยท View on GitHub
This demonstrates reactivity between Component in miso. See live here
Example
childComponent :: MisoString -> Component ParentModel ChildModel ChildAction
childComponent childComponentName = (component (ChildModel 0) noop view_)
{ bindings =
[ parentField <---> childField
-- ^ dmj: Bidirectional synch between parent and child `model`, using `Lens`
]
} where
view_ :: ChildModel -> View ChildModel ChildAction
view_ (ChildModel x) =
div_
[]
[ h3_ [] [ text ("Child Component " <> childComponentName) ]
, button_ [ onClick ChildAdd ] [ "+" ]
, text (ms x)
, button_ [ onClick ChildSubtract ] [ "-" ]
]
Introduction
As of 1.9, miso is now recursive. This means miso applications can embed other miso applications, and be distributed independently. The type Component has been introduced to facilitate this and is equipped with lifecycle mounting hooks (mount / unmount). This has necessitated a runtime system to manage Component internally. Component are now parameterized by parent, which is the type of the ancestor's model. Component model can be synchronized between the parent / child relationship (unidirectionally or bidirectionally) in a type-safe, composable manner.
miso has added the bindings field to establish edges in the Component graph (between immediate ancestor and descendant). This allows effects to be applied from one Component model to the next, along the user-defined edge (Binding) via a Lens. When used at multiple levels in the tree this can create a cascading effect.
The -->, <--, <--> reactive combinators have been introduced to allow users to establish edges between Component in the graph, in a declarative way. This creates dependencies in the graph between Component model changes. The combinators take two Lens as arguments, which synchronize changes between Component model in the direction the user desires.
Under the hood this is done through a scheduler and a depth-first traversal of the Component graph. This is accomplished without imposing a recursive interface on end users (miso handles all the recursion under the hood).
Furthermore, miso allows declarative upstream communication with the parent. Whereas in React a callback would need to be passed to the child to invoke parent model changes, creating a more convoluted programming model. A bidirectional synch can also be established between parent and child using the (<-->) combinator. This can allow sibling communication, where the parent is used as a proxy (as seen in the example).
Development
The source maintains an example of sibling communication using the <--> reactive combinator.
Tip
This requires installing nix with Nix Flakes enabled. Although not required, we recommend using miso's binary cache.
Call nix develop to enter a shell with GHC 9.12.2
$ nix develop --experimental-features nix-command --extra-experimental-features flakes
Once in the shell, you can call cabal run to start the development server and view the application at http://localhost:8080
Build (Web Assembly)
$ nix develop .#wasm --command bash -c "make"
Build (JavaScript)
$ nix develop .#ghcjs --command bash -c "build"
Serve
To host the built application you can call serve
$ nix develop .#wasm --command bash -c "serve"
Clean
$ nix develop .#wasm --command bash -c "make clean"
This comes with a GitHub action that builds and auto hosts the example.