miso
Copyright(C) 2016-2026 David M. Johnson (@dmjio)
LicenseBSD3-style (see the file LICENSE)
MaintainerDavid M. Johnson <code@dmj.io>
Stabilityexperimental
Portabilitynon-portable
Safe HaskellNone
LanguageHaskell2010

Miso

Description

miso 🍜

miso is a library for building web and native user interface applications in Haskell. See the GitHub group.

It provides a React-like programming experience for a simple Haskell dialect that emphasizes

  • performance
  • purity
  • simplicity
  • extensibility
  • composability

miso addresses common areas of web development:

  • DOM manipulation: miso uses a Virtual DOM with a diffing algorithm that is responsible for all DOM modification and Component lifecycle hooks.
  • Event delegation: All event listeners are attached to a top-level element (typically <body>). When raised, events are routed through the virtual DOM to Haskell event handlers which cause application state changes. Internally miso virtualizes both the capture and bubble phases of the browser when it performs event routing.
  • Prerendering: Prerendering is a process where the server delivers HTML to the client before the JavaScript (or WebAssembly) application bootstraps. Instead of performing an initial draw, the application will create and populate the virtual DOM from the actual DOM. This is a process known as "hydration". This avoids unnecessary page draws on initial page load and enhances search engine optimization. miso provides its own HTML rendering (Miso.Html.Render) to render HTML on the server and the miso function exists on the client to "hydrate" the virtual DOM with the DOM.
  • Components: A Component can be considered an instance of a miso application. A Component contains user-defined state, logic for updating this state, and a function for creating UI templates from this user-defined state. Component can nest other Component, enabling arbitrarily deep UI trees.
  • Custom renderers: The underlying DOM operations are able to be abstracted. This allows a custom rendering engine to be used. This is seen in the miso-lynx project (which allows miso to target mobile phone devices).
  • Lifecycle hooks: Component expose mount and unmount lifecycle hooks. This allows users to define custom logic that will execute when a Component mounts or unmounts. onCreated and onDestroyed are VNode specific lifecycle hooks. These hooks are commonly used for Component communication and for third-party integration with JavaScript libraries.
  • State management: Component model state can be manipulated using Miso.Lens or Miso.State in response to application events.

Architecture

  • React: miso implements a subset of the React architecture including Component, Lifecycle Hooks, Virtual DOM, Event delegation, Fragment and Props.
  • Elm: miso also implements the Elm architecture (MVU) and the mailbox communication pattern.

The Model-View-Update pattern

The core type of miso is Component. The Component API adheres to the Elm MVU (model-view-update) interface. This is similar to a left-fold over actions — the Component model is updated by update and rendered by view.

  • model: This can be any user-defined type in Haskell. An Eq constraint is required. We recommend using the default derived Eq instance.
  • view: view :: props -> model -> View model action This is the templating function that is used to construct a new virtual DOM (or HTML if rendering on the server).
  • update: update :: action -> Effect parent props model action The update function handles how the model evolves over time in response to events that are raised by the application. This function takes any action, updating the model and optionally introduces IO into the system.

Your first Component

To define a Component, the component smart constructor can be used. Below is an example of a simple counter Component.

-----------------------------------------------------------------------------
module Main where
-----------------------------------------------------------------------------
import Miso
import Miso.Lens
import qualified Miso.Html.Element as H
import qualified Miso.Html.Event as HE
import qualified Miso.Html.Property as HP
-----------------------------------------------------------------------------
                      * - The type of the parent Component model
                      |    * - The type of the parent Component props accessible to the child
                      |    |   * - The type of the current Component's model
                      |    |   |   * - The type of the action that updates the model
                      |    |   |   |
counter :: Component ROOT () Int Action
counter = vcomp m u v
  where
    m :: Int
    m = 0
                            * - The type of the parent Component model
                            |   * - The type of the parent Component props accessible to the child
                            |   |   * - The type of the current Component's model
                            |   |   |   * - The type of the action that updates the model
                            |   |   |   |
    u :: Action -> Effect ROOT () Int Action
    u = \case
      Add -> this += 1
      Subtract -> this -= 1

    v :: () -> Int -> View Int Action
    v _ x = vfrag
      [ H.button_ [ HE.onClick Add, HP.id_ "add" ] [ "+" ]
      , text (ms x)
      , H.button_ [ HE.onClick Subtract, HP.id_ "subtract" ] [ "-" ]
      ]
-----------------------------------------------------------------------------
main :: IO ()
main = startApp defaultEvents counter
-----------------------------------------------------------------------------
data Action
  = Add
  | Subtract
  deriving (Eq, Show)
-----------------------------------------------------------------------------

Running your first Component

The startApp (or miso) functions are used to run the above Component.

main :: IO ()
main = startApp defaultEvents counter

We recommend startApp as the starting point — it sets up event listeners, performs the initial page draw, and assumes <body> is empty.

The miso function (and prerender) assume that <body> has already been populated by the results of the view function. Instead of drawing, miso will perform hydration. If the structures do not match, miso will fall back to drawing the page from scratch (clearing the contents of <body> first).

It is possible to execute an initial action when a Component is first mounted. See the mount (and similarly unmount) hooks.

data Action = Init

main :: IO ()
main = startApp defaultEvents counter { mount = Just Init }

update :: App model Action
update = \case
  Init -> io_ (consoleLog "hello world!")

Note also the signature of startApp.

startApp :: Eq model => Events -> App model action -> IO ()

The App type synonym is defined as:

type App model action = Component ROOT () model action

ROOT is a type tag for top-level Component — one with no parent.

data ROOT

startApp and miso will always infer parent as ROOT.

View DSL

The View type represents the virtual DOM — a Rose tree of nodes mutually recursive with Component via the view function.

data View model action
  = VNode Namespace Tag [Attribute action] [View model action]
  | VText (Maybe Key) MisoString
  | VComp (Maybe Key) (SomeComponent model)
  | VFrag (Maybe Key) [View model action]

VNode and VText have a one-to-one mapping from the virtual DOM to the physical DOM. The VComp and VFrag constructors are abstract (live only on the virtual DOM) and do not contain a reference to the physical DOM. The existential SomeComponent is what allows embedding polymorphic Component within a View.

data SomeComponent parent
  = forall model action props . (Eq model, Eq props)
  => SomeComponent props (Component parent props model action)

The smart constructors:

A full list of element smart constructors built on node (e.g. div_) can be found in Miso.Html.Element.

VComp

Composition

miso Component can contain other Component. This is accomplished through the Component mounting combinator (+>). This combinator is responsible for encoding a typed Component hierarchy, allowing Component type-safe read-only access to their parent model state.

This combinator unifies the parent model with the child parent, and subsequently the grandchild parent unifies with the child model. This gives us a correct-by-construction Component hierarchy.

(+>)
  :: forall child model action a . Eq child
  => MisoString
  -> Component model () child action
  -> View model a
key +> vcomp = VComp (Just (toKey key)) (SomeComponent () vcomp)

Practically, using this combinator looks like:

view :: props -> Int -> View Int action
view _ _ = div_ [ id_ "container" ] [ "counter" +> counter ]

The "counter" string is a unique Key that identifies the Component at runtime. These keys are very important when diffing two Component together. When intentionally replacing Component it is important to specify a new Key, otherwise the Component will not be unmounted.

It is possible to mount a component using the mount_ function, which avoids specifying a key_, but this should only be used when the user is certain they will not be diffing their Component with another Component. When in doubt, use the (+>) combinator and key_ your Component.

Lifecycle hooks

Components are mounted during diffing. All Component are equipped with mount and unmount hooks, allowing custom actions to be dispatched in response to lifecycle events.

VNode (Element nodes)

A VNode represents a DOM element node — the most common kind of virtual DOM node. It carries a Namespace, a tag name, a list of Attribute values, and a list of child View nodes:

VNode HTML "div" [ id_ "container" ] [ "Hello, world!" ]

In practice you will rarely construct VNode directly. Instead use the element smart constructors from Miso.Html.Element, which fix the namespace and tag for you:

div_    [ id_ "container" ] [ "Hello, world!" ]
button_ [ onClick DoSomething ] [ "Click me" ]
h1_     [ className "title" ] [ text (ms pageTitle) ]

For elements not covered by Miso.Html.Element, use node (or its synonym vnode) directly:

node HTML "details" [] [ node HTML "summary" [] [ "More info" ] ]

SVG and MathML elements use the SVG and MATHML namespaces respectively, and are covered by the smart constructors in Miso.Svg.Element and Miso.Mathml.Element.

Unlike VComp and VFrag, VNode has a one-to-one correspondence with a physical DOM element: each VNode in the virtual DOM maps to exactly one element in the browser.

The smart constructors for VNode are:

Lifecycle hooks

Like Component, VNode elements expose lifecycle hooks.

These are useful for initializing and tearing down third-party libraries, as in the example below using highlight.js

{-# LANGUAGE QuasiQuotes -#}
{-# LANGUAGE MultilineStrings -#}

import Miso
import Miso.FFI.QQ (js)

data Action = Highlight DOMRef

update :: Action -> Effect parent props model Action
update = \case
  Highlight domRef -> io_ $ do
    [js| hljs.highlight(${domRef}) |]

view :: props -> model -> View model Action
view _ x =
  code_
  [ onCreatedWith Highlight
  ]
  [ """
    function addOne (x) { return x + 1; }
    """
  ]

As a convention, the *with variant of VNode lifecycle hooks (e.g. onCreatedWith) provides the target DOMRef in the callback.

VText (Text nodes)

A VText node represents a DOM text node. Unlike VComp and VFrag, VText has a one-to-one correspondence with a physical DOM node: each VText in the virtual DOM maps to exactly one Text node in the browser.

The simplest way to produce a VText is via the IsString instance on View model action. String literals inside a child list are automatically promoted to VText nodes without any extra imports:

div_ [] [ "Hello, world!" ]

For dynamic content, use the text smart constructor with a MisoString:

div_ [] [ text (ms userName) ]

HTML Encoding

When compiling with the ssr flag (server-side rendering), text automatically HTML-encodes its argument — <, >, &, ", and ' are replaced with their respective HTML entities. This prevents accidental XSS when rendering user-supplied strings on the server.

-- SSR output: &lt;b&gt;bold&lt;/b&gt;
text "<b>bold</b>"

To embed pre-rendered or trusted content without escaping, use textRaw. It is a no-op on the client and bypasses encoding on the server:

textRaw "<b>bold</b>"   -- server and client: <b>bold</b>

Concatenating Multiple Strings

text_ accepts a list of MisoString values and joins them with a single space, which is useful when building text from multiple pieces without manual concatenation:

-- Renders: Hello world
div_ [] [ text_ [ Hello, "world" ] ]

Keyed Text Nodes

A VText may optionally carry a Key. Keyed text nodes participate in the same reconciliation algorithm as keyed VNode and VFrag nodes. Providing a stable key lets the differ identify the node across renders, preventing unnecessary DOM text node replacement when sibling order changes.

ul_ [] (map renderItem items)

renderItem :: Item -> View model Action
renderItem item = li_ [] [ textKey (itemId item) (itemLabel item) ]

keyed can also attach a key to any existing View, including a VText produced by a string literal or text:

keyed "greeting" ("Hello!" :: View model action)

The smart constructors for VText are:

  • text — single string, HTML-encoded on the server
  • vtext — synonym for text
  • textRaw — single string, never HTML-encoded
  • text_ — list of strings joined with a space
  • textKey — single keyed string
  • textKey_ — list of keyed strings joined with a space
  • keyed — attach a key to any View, including VText

VFrag (Fragment nodes)

VFrag groups sibling nodes without a wrapper element in the DOM, analogous to the React Fragment API (<></>) and the browser's DocumentFragment.

-- Renders two <li> elements as direct siblings, no enclosing element
fragment [ li_ [] [ text "Item A" ], li_ [] [ text "Item B" ] ]

A VFrag may optionally carry a Key. Keyed fragments participate in the same reconciliation algorithm as keyed VNode and VText nodes, allowing the virtual DOM differ to identify, reorder, and reuse groups of siblings efficiently.

-- Keyed fragment — survives reordering without full teardown/remount
vfrag_ "my-key" [ li_ [] [ text "Item A" ], li_ [] [ text "Item B" ] ]

Fragments may be nested — a VFrag child may itself be a VFrag. The diff function recurses into nested fragments and processes all fragments as if they were a flat sequence of sibling DOM nodes, so nesting carries no runtime cost beyond the extra VFrag constructor allocation.

Empty fragments (fragment []) in child nodes are erased from the virtual DOM tree in the Haskell layer before they reach diffing in JavaScript and are therefore a no-op.

The smart constructors for VFrag are:

  • fragment — unkeyed fragment
  • vfrag — unkeyed fragment (alias)
  • fragment_ — keyed fragment
  • vfrag_ — keyed fragment (alias, infix-friendly: "key" `vfrag_` [...])

Key

A Key is a unique identifier used to optimize diffing.

Virtual DOM nodes can be "keyed" (See key_). Keys have multiple meanings in miso (and react).

  • Keys are used to optimize child node list diffing.

When two lists of elements are being diffed, as long as they all have unique keys, diffing large child lists will be much faster. This optimization automatically occurs when all the elements in a VNode child list contain unique keys. Unless all View nodes in a child list are keyed, this optimization will not fire.

  • Keys are used to compare two identical nodes.

If two VNode are being compared (or two VComp) and their keys differ, the old node will be destroyed and a new one created. Otherwise, the underlying DOM node won't be removed, but its properties will be diffed. In the case of diffing two Component (the VComp case), if the keys differ, the unmount phase will be triggered for the old VComp and the mount phase will be triggered for the new Component. The underlying DOM reference will be replaced.

See the key_ property for usage (and smart constructors like textKey_ and (+>) as well).

ul_
  []
  [ li_ [ key_ "key-1" ] [ "a" ]
  , li_ [ key_ "key-2" ] [ "b" ]
  , "key-3" +> counter
  , textKey "key-4" "text here"
  , vfrag_ "key-5" [ "foo", "bar" ]
  ]

Events

  • Event Delegation

By default all events are delegated through <body>. Miso supports both capture and bubble phases of browser events. Users can handle both phases in their applications.

  • Using events

Miso exposes a defaultEvents for convenience, these events are commonly used events and listened for on <body>. They get routed through the View to the virtual DOM node that raised the event. Other Events are exposed as conveniences (e.g. touchEvents). All events required by all Component must be combined together for use when running your application (e.g. keyboardEvents <> touchEvents).

touchEvents :: Events
touchEvents = M.fromList
  [ ("touchstart", BUBBLE)
  , ("touchcancel", BUBBLE)
  , ("touchmove", BUBBLE)
  , ("touchend", BUBBLE)
  ]
  • Defining event handlers

Users can define their own event handlers using the on combinator. By default this will define an event in the BUBBLE phase. See onCapture for handling events during the CAPTURE phase. See the module Miso.Html.Event for many predefined events.

onChangeWith :: (MisoString -> DOMRef -> action) -> Attribute action
onChangeWith = on "change" valueDecoder

The *with variant of events (e.g. onChangeWith) provides the target DOMRef in the callback function.

  • Decoding events

After an event has been raised, one can extract information from the event for use in their application. This is accomplished through a Decoder. Many common decoders are available for use in Miso.Event.Decoder.

data Decoder a
  = Decoder
  { decoder :: Value -> Parser a
  , decodeAt :: DecodeTarget
  }

-- | Example of a custom Decoder for the value property of an event target.
valueDecoder :: Decoder MisoString
valueDecoder = Decoder {..}
  where
    decodeAt = DecodeTarget ["target"]
    decoder = withObject "target" $ \o -> o .: "value"

Attributes / Properties

The Attribute type carries everything that can be attached to a DOM element:

data Attribute action
  = Property MisoString Value          -- ^ DOM property (key/value)
  | ClassList [MisoString]                         -- ^ CSS class list
  | On (Sink action -> ...)                        -- ^ Event handler
  | Styles (Map MisoString MisoString) -- ^ Inline style map

In practice you never construct these directly. Use the smart constructors from Miso.Html.Property, Miso.Html.Event, Miso.Property, and Miso.CSS:

div_
  [ id_ "container"                    -- textProp "id"
  , className "card active"            -- textProp "class"
  , classList ["card", "active"]       -- ClassList (alternative)
  , disabled_                          -- boolProp "disabled" True
  , onClick MyAction                   -- On event handler
  , style_ [ display "flex" ] -- Styles map
  ]
  []

Custom properties

Use prop (or the typed variants textProp, boolProp, intProp, doubleProp, objectProp) from Miso.Property to set arbitrary DOM properties:

prop "data-index" (42 :: Int)      -- sets element.data-index = 42
textProp "placeholder" "Search…"  -- sets element.placeholder
boolProp "checked" True            -- sets element.checked = true

Note that DOM properties and HTML attributes are distinct. Miso sets properties on the DOM node object (e.g. node.checked) rather than the HTML attribute (e.g. setAttribute("checked", ...)). This matches what the browser actually exposes in JavaScript and avoids common pitfalls with boolean attributes.

Keys

key_ (and its alias keyProp) attaches a reconciliation key to any element. See the Key section for details.

li_ [ key_ (itemId item) ] [ text (itemLabel item) ]

Effect

The Effect type is used to mutate the model over time in response to action. Effect also allows IO to be scheduled for evaluation by the miso scheduler.

Note: IO is never evaluated inside of Effect, it is only scheduled. There is no MonadIO instance for Effect.

The Effect type is defined as a RWS.

type Effect parent props model action = RWS (ComponentInfo parent props) [Schedule action] model ()

IO can be performed either synchronously or asynchronously. By default all IO is asynchronous

Asynchronous IO

  • io: Used to introduce asynchronous io into the system, see also the io_ variant.
  • withSink: The core function (from which most other combinators are defined) that gives users access to the underlying event Sink. This also allows us to introduce IO into the system. The miso scheduler attaches exception handlers to all IO actions.
  • For maximum flexibility, the MonadWriter instance (tell) can be used to schedule IO (see the withSink implementation).

Synchronous IO

  • sync: Forces the scheduler to evaluate IO synchronously. It is recommended to use the io function by default, sync *will* block the scheduler.

Sink

type Sink action = action -> IO ()

The Sink function allows one to write any action to the global event queue. See withSink for more information.

Managing model state.

Any MonadState function is allowed for use when manipulating model, get, put, etc. See Miso.State.

The MonadReader instances allows the retrieval of ComponentInfo within Effect. ComponentInfo provides the current ComponentId the parent ComponentId, and the DOMRef (_componentDOMRef) that the Component is mounted on.

Component communication

Miso provides three mechanisms for Component to exchange data:

Props

Inspired by React props, miso allows a parent Component to pass read-only data down to a child Component via a mechanism called props (short for properties).

Props vs. Component-local state

  • model: Component-local state. It is owned and mutated exclusively by the Component itself through its update function. No other Component can write to it directly.
  • props: Data inherited from the parent Component. Props flow downward through the component hierarchy and are read-only from the child's perspective. The parent decides what props to pass at mount time; the child cannot mutate them. Props that change in a the parent cause the child to re-render.

This mirrors the distinction in React between component state (useState) and props received from above (function MyComponent({ name }) { ... }).

When to use props

Props are best suited for metadata — contextual or configuration data that the child needs to know about but should not own. Good examples: a user's display name, a theme token, a locale string, or a read-only identifier used to customise rendering.

If the data drives the child's own business logic — counters it increments, form fields it edits, async state it manages — that data belongs in the child's model instead. Putting mutable business-logic state in props would require the parent to own and thread through every change, creating unnecessary coupling. Prefer props for "what the child should know" and model for "what the child should do".

Props in view

The view field of a Component always takes props as its first argument:

view :: props -> model -> View model action

Top-level applications have no parent, so props is always ():

view :: () -> model -> View model action
view _props model = …

Props in Effect / update

Use getProps inside the Effect monad to read the current value of props:

update :: Action -> Effect parent props Model Action
update = \case
  SomeAction -> do
    p <- getProps
    io_ (consoleLog (ms (show p)))

Alternatively, use the view combinator with the props lens:

update = \case
  SomeAction -> do
    p <- view props

ROOT — the top-level Component

When a Component is passed to startApp (or miso) it has no parent. The parent type is specialized to ROOT and props is fixed to ():

type App model action = Component ROOT () model action

Because there is no parent to inherit from, props will always be () for a root-level Component. You can simply ignore the first argument in view and skip getProps in update.

Passing props to a child Component

Use mountWithProps_ (keyed) or mountWithProps (unkeyed) in the parent's view to mount a child and supply its props:

mountWithProps_
  :: (Eq child, Eq props)
  => MisoString
  -> props
  -> Component parent props child action
  -> View parent a

Example: child reading parent-supplied props

The following shows a parent Component that maintains a greeting string in its model and passes it as props to a child Component. The child renders the greeting and can also read it from within its update function.

-----------------------------------------------------------------------------
-- The props type: what the parent shares with the child
newtype Greeting = Greeting MisoString deriving (Eq)
-----------------------------------------------------------------------------
-- Child component
--
--                  parent      props    model  action
--                  |           |        |      |
child :: Component ParentModel Greeting ()     ChildAction
child = vcomp () updateChild viewChild
  where
    viewChild :: Greeting -> () -> View () ChildAction
    viewChild (Greeting g) _ =
      div_ [] [ text ("Hello, " <> g <> "!") ]

    updateChild :: ChildAction -> Effect ParentModel Greeting () ChildAction
    updateChild = \case
      ReadGreeting -> do
        Greeting g <- getProps
        io_ (consoleLog g)
-----------------------------------------------------------------------------
-- Parent component: owns the greeting, passes it to the child as props
parent :: App ParentModel ParentAction
parent = vcomp (ParentModel World) noop viewParent
  where
    viewParent :: () -> ParentModel -> View ParentModel ParentAction
    viewParent _ (ParentModel g) =
      mountWithProps_ "child" (Greeting g) child
-----------------------------------------------------------------------------
newtype ParentModel = ParentModel MisoString deriving (Eq)
data ChildAction = ReadGreeting
data ParentAction

A few things to notice:

  • The child's parent type parameter is ParentModel. This must match the parent Component model type — mountWithProps_ enforces this at compile time.
  • getProps inside the child's update yields a Greeting, not the full ParentModel. The child only sees what the parent explicitly chose to share.
  • The root App always has props ~ (); no extra plumbing is needed when calling startApp.

Asynchronous communication

Every Component has a mailbox — a slot that receives Value messages sent by other components. Messages are dispatched asynchronously via the event queue.

Sending

Receiving with checkMail

Wire up the mailbox field on Component using checkMail, which handles JSON parsing and routes to success/error actions:

data Action
  = ReceivedMsg MyMsg
  | MailError   MisoString

myComp :: Component parent props model Action
myComp = (vcomp m u v)
  { mailbox = checkMail ReceivedMsg MailError }

Looking up a ComponentId

Inside Effect, use ask to obtain a ComponentInfo:

update = \case
  SendMsg targetId -> do
    io_ (mail targetId ("hello" :: MisoString))
  GetMyId -> do
    info <- ask
    let myId = _componentInfoId info
    ...
  • Miso.PubSub — publish/subscribe pattern for fan-out messaging across unrelated components.

Synchronous communication

Experimental support for data bindings (where Component model can synchronize fields via a Lens in response to model differences along the parent-child relationship). See the Miso.Binding module for more information, and the miso-reactive example. *Warning*: This is still considered experimental.

Parent access

While not direct communication, a Component can asynchronously receive read-only access to its parent state via the parent function.

Subscriptions

A Sub is any long-running operation that is external to a Component, but that can write to a Component Sink. Sub come in two flavors, a dynamic Sub (via startSub / stopSub) and subs.

main :: IO ()
main = startApp defaultEvents app { subs = [ timerSub ] }

timerSub :: Sub Action
timerSub sink = forever $ (threadDelay 100000) >> sink Log

data Action = Log

The subs field of Component contains Sub that exist for the lifetime of that Component. When a Component unmounts, these Sub will be stopped, and their resources finalized.

onLineSub :: (Bool -> action) -> Sub action
onLineSub f sink = createSub acquire release sink
  where
    release (cb1, cb2) = do
      windowRemoveEventListener "online"  cb1
      windowRemoveEventListener "offline" cb2
    acquire = do
      cb1 <- windowAddEventListener "online"  (const $ sink (f True))
      cb2 <- windowAddEventListener "offline" (const $ sink (f False))
      pure (cb1, cb2)

At times its necessary to dynamically generate a Sub in reponse to an event (e.g. starting a Miso.WebSocket connection when a user logs in). The startSub and stopSub functions facilitate dynamic Sub creation / removal.

update = \case
  StartTimer -> startSub ("timer" :: MisoString) timerSub
  StopTimer -> stopSub "timer"
  Log -> io_ (consoleLog "log")
    where
      timerSub :: Sub Action
      timerSub sink = forever $ (threadDelay 100000) >> sink Log

data Action = Log

createSub is a helper function for creating a Sub using the bracket pattern. This ensures that event listeners can be unregistered when a Component unmounts. For example usage please see the Miso.Subscription sub modules. createSub is only meant to be used in scenarios where custom event listeners are required.

State management

Miso bundles a lightweight lens library in Miso.Lens to minimise dependencies and payload size. Any lens library (optics, lens) also works — Miso.Lens is not required.

Basic lens operations

view l   -- read a field (MonadReader)
set  l v           -- write a field
over l f           -- modify a field
r ^. l             -- infix read
r & l .~ v       -- infix write

MonadState operators (for use inside Effect)

l += n   -- increment a numeric field
l -= n   -- decrement
l *= n   -- multiply

this — the identity lens

When the model is the field (e.g. the model is a plain Int), use this:

update = \case
  Increment -> this += 1
  Decrement -> this -= 1

Generating lenses

Three approaches, pick one:

  • Template Haskell (Miso.Lens.TH): makeLenses / makeClassy splice lenses for each record field.
{-# LANGUAGE TemplateHaskell #-}
import Miso.Lens.TH (makeLenses)

data Model = Model { _count :: Int, _name :: MisoString }
makeLenses ''Model

update = \case
  Increment -> count += 1
  Rename n  -> name .= n
  • Generics (Miso.Lens.Generic): field / HasLens derive lenses at compile time using GHC.Generics — no TH splice required. Requires TypeApplications and, optionally, OverloadedLabels for the #field shorthand.
{-# LANGUAGE DataKinds          #-}
{-# LANGUAGE DeriveGeneric      #-}
{-# LANGUAGE OverloadedLabels   #-}
{-# LANGUAGE TypeApplications   #-}
import GHC.Generics (Generic)
import Miso.Lens.Generic (field)

data Model = Model { count :: Int, name :: MisoString }
  deriving (Eq, Generic)

update = \case
  Increment -> field @"count" += 1          -- via TypeApplications
  Rename n  -> #name .= n                     -- via OverloadedLabels
  • Hand-written: construct a Lens directly using lens and the Lens s a synonym.
name :: Lens Person MisoString
name = lens _name $ \p n -> p { _name = n }

(2D/3D) Canvas support

Miso has full 2D and 3D canvas support via Miso.Canvas. See also the miso-canvas example and the three-miso package for Three.js integration.

The Canvas monad

Drawing commands run in the Canvas monad, which is a ReaderT over a CanvasContext2D (the raw JavaScript CanvasRenderingContext2D):

type Canvas a = ReaderT CanvasContext2D IO a

Embedding a canvas in the view

Use the canvas smart constructor. It takes an init callback (runs once on mount, returns state) and a draw callback (runs on every render with the current state):

canvas
  [ HP.width_ "800", HP.height_ "480" ]
  (\_ -> pure ())                   -- init: called once on canvas initialization
  (\() -> drawScene myModel)        -- draw: called many times, on each diff.

canvas_ is the variant that threads no init state at all (always passes ()).

Drawing commands

Common 2D primitives:

drawScene :: Model -> Canvas ()
drawScene model = do
  clearRect (0, 0, 800, 480)
  fillStyle (RGB 30 144 255)
  beginPath ()
  arc (400, 240, 50, 0, 2 * pi)
  fill ()
  font "24px sans-serif"
  fillText ("Score: " <> ms (score model), 10, 30)

Available primitives include: clearRect, fillRect, strokeRect, beginPath, closePath, moveTo, lineTo, arc, arcTo, fill, stroke, fillText, drawImage.

Style setters: fillStyle, strokeStyle, lineWidth, font. fillStyle and strokeStyle accept a StyleArg — use color (not color) to construct one from a Color value. See the Canonical Import Pattern section for how to avoid the name collision between color and color.

Animation loop

For smooth 60 FPS canvas animations, use rAFSub from Miso.Subscription.RAF instead of a manual threadDelay loop. It hooks into the browser's requestAnimationFrame API and delivers a DOMHighResTimeStamp (milliseconds) on each frame:

data Action = Tick Double

main :: IO ()
main = startApp defaultEvents comp { subs = [ rAFSub Tick ] }

HTML

Miso's View type doubles as an HTML serialiser via the ToHtml class in Miso.Html.Render. This is used for server-side rendering (SSR): build a View with the normal DSL and render it to a lazy ByteString on the server.

class ToHtml a where
  toHtml :: a -> ByteString

Instances are provided for View m a and [View m a]:

import Miso.Html.Render (toHtml)

pageHtml :: ByteString
pageHtml = toHtml $ div_ [ id_ "root" ] [ "Hello, world!" ]

This is typically wired into a Servant handler on the server using the servant-miso-html package, which provides an HTML content-type that serialises View and Component values directly — no manual ByteString conversion needed:

import Servant.Miso.Html (HTML)

type Home    = "home"    :> Get '[HTML] (Component model action)
type About   = "about"   :> Get '[HTML] (View model action)
type Contact = "contact" :> Get '[HTML] [View model action]
type API = Home :<|> About :<|> Contact

On the client, pass the matching Component to miso (instead of startApp) so it hydrates the server-rendered markup rather than redrawing from scratch. See the Prerendering section for the full flow.

JavaScript EDSL

Miso.DSL provides a JavaScript DSL inspired by jsaddle for interacting with the browser from Haskell.

Key operators

  • (!) — property access: obj ! "key" reads obj.key
  • (#) — method call: obj # "method" args calls obj.method(args)
  • jsg — access a global JS variable by name
  • jsgf — call a global JS function by name with arguments
-- Read document.body.children.length
document <- jsg "document"
len :: Int <- fromJSValUnchecked =<< (document ! "body" ! "children" ! "length")

-- Call console.log("hello")
console <- jsg "console"
console # "log" $ ["hello" :: MisoString]

Marshalling

ToJSVal converts Haskell values to JSVal for passing into JavaScript. FromJSVal converts JSVal back to Haskell. fromJSValUnchecked throws on failure; use fromJSVal for a safe Maybe variant.

QuasiQuotation (inline-js)

Miso.FFI.QQ provides the js QuasiQuoter for embedding inline JavaScript directly in Haskell source. Any Haskell binding in scope can be interpolated into the JavaScript body with ${varName} syntax — miso uses the binding's ToJSVal instance to marshal it across the boundary at runtime.

{-# LANGUAGE QuasiQuotes #-}

import Miso.FFI.QQ (js)

-- Fire-and-forget: pass a value to a JS library
update :: Action -> Effect parent props model Action
update = \case
  Log msg -> io_ [js| console.log(${msg}) |]

data Action = Log MisoString

Returning values from JavaScript

The return type is inferred from the call site via FromJSVal. Use an explicit type annotation or a do-binding to drive inference:

fac :: Int -> IO Int
fac n = [js|
  let x = 1;
  for (let i = 1; i <= ${n}; i++) { x *= i; }
  return x;
|]

Haskell variables referenced inside the quoter must be in scope at the splice site; the compiler will report an error if a ${name} has no corresponding binding.

Routing

Miso.Router provides a reversible, type-safe client-side router. A Route type encodes URL structure; the Router class converts between routes and URI values in both directions. Use it with routerSub or uriSub to react to browser navigation.

Defining a Router with Generics

Derive Router via GHC.Generics — constructor names become path segments (camel-case uses only the first hump). Use Capture, Path, QueryParam, and QueryFlag as constructor fields to describe the URL shape:

{-# LANGUAGE DeriveGeneric  #-}
{-# LANGUAGE DeriveAnyClass #-}

import GHC.Generics
import Miso.Router

data Route
  = Index                                                      -- matches "/"
  | About                                                      -- matches "/about"
  | Product (Capture "id" Int) (QueryParam "tab" MisoString)   -- matches "product42?tab=info"
  deriving stock    (Show, Eq, Generic)
  deriving anyclass Router

The router is reversibleprettyRoute re-serialises any route back to a URL:

prettyRoute (Product (Capture 42) (QueryParam (Just "info")))
-- "/product/42?tab=info"

Defining a Router manually

For full control, implement routeParser and fromRoute directly:

data Route = Product Int

instance Router Route where
  routeParser = routes [ Product <$> (path "product" *> capture) ]
  fromRoute (Product n) = [ toPath "product", toCapture n ]

Subscribing to URI changes

routerSub listens to popstate events and delivers the parsed route (or a RoutingError) to your update function:

app = (vcomp m u v) { subs = [ routerSub HandleRoute ] }

update = \case
  HandleRoute (Right Index)       -> modify (\m -> m { page = HomePage })
  HandleRoute (Right About)       -> modify (\m -> m { page = AboutPage })
  HandleRoute (Left _)            -> modify (\m -> m { page = NotFound })

uriSub is the lower-level variant — it delivers the raw URI without parsing, useful when you want to handle routing yourself.

Navigating programmatically

pushURI    uri    -- push a raw URI onto the History stack
pushRoute  route  -- push a typed route (serialised via Router)
replaceURI uri    -- replace the current history entry
back              -- go back one entry
forward           -- go forward one entry

Type-safe links in views

href_ produces a type-safe href attribute from any route:

button_ [ href_ (Product (Capture 10) (QueryParam Nothing)) ] [ "Go to product 10" ]

MisoString

MisoString is miso's canonical string type, chosen to minimise copying between the Haskell and JavaScript heaps:

  • JS / WASM backends: MisoString is JSString, a direct reference to a JavaScript string — no marshalling cost when passing to the DOM or FFI.
  • Server / vanilla GHC (ssr flag): MisoString is Text.

Use MisoString anywhere you would otherwise reach for String or Text in a miso application. See Miso.String for the full API.

Converting to MisoString

The ms function (shorthand for toMisoString) converts any type with a ToMisoString instance:

ms "hello"          -- String    -> MisoString
ms (42 :: Int)      -- Int       -> MisoString
ms (3.14 :: Double) -- Double    -> MisoString
ms myText           -- Data.Text -> MisoString

ToMisoString instances are provided for String, Text, Text, ByteString, Int, Word, Double, Float, and Char.

Converting from MisoString

fromMisoString parses a MisoString back into another type (throws on failure). Use fromMisoStringEither for a safe variant:

fromMisoString "42"     :: Int     -- 42
fromMisoString "3.14"   :: Double  -- 3.14
fromMisoStringEither s  :: Either String Int

FromMisoString instances are provided for String, Text, Text, ByteString, Int, Word, Double, and Float.

Multiline literals

Miso.String.QQ provides a QuasiQuoter for multiline MisoString literals:

{-# LANGUAGE QuasiQuotes #-}

import Miso.String.QQ (misoString)

snippet :: MisoString
snippet = [misoString|
  line one
  line two
|]

MisoString is also the element type used throughout Miso.Util.Lexer and Miso.Util.Parser.

JSON

Miso.JSON is a microaeson-inspired JSON library specialised to MisoString. On the JS/WASM backends it delegates encoding and decoding to the JavaScript runtime (JSON.stringify / JSON.parse) for performance. On the server (ssr flag) it uses a pure Haskell implementation. Miso.JSON is used internally by Miso.Event.Decoder, Miso.Fetch, and Miso.WebSocket.

Value

The JSON Value type mirrors the JSON specification:

data Value
  = Number Double
  | Bool   Bool
  | String MisoString
  | Array  [Value]
  | Object Object
  | Null

Encoding

Encode any ToJSON instance to a MisoString:

encode value        -- uses JS runtime on client, pure on server
encodePure value    -- always uses pure Haskell implementation

Decoding

decode s            :: Maybe a      -- returns Nothing on failure
eitherDecode s      :: Either MisoString a

ToJSON / FromJSON

Derive instances via GHC.Generics:

{-# LANGUAGE DeriveGeneric #-}

import GHC.Generics
import Miso.JSON

data User = User { name :: MisoString, age :: Int }
  deriving (Generic)

instance ToJSON   User
instance FromJSON User

Use genericToJSON / genericParseJSON with Options to customise field and constructor names. camelTo2 is provided for converting camelCase to snake_case (or any separator):

instance ToJSON User where
  toJSON = genericToJSON defaultOptions { fieldLabelModifier = camelTo2 '_' }

Building and Parsing Objects

-- Build
object [ "name" .= ms Alice, "age" .= (30 :: Int) ]

-- Parse (inside a withObject callback or event decoder)
withObject User $ \o -> User
  <$> o .: "name"     -- required field
  <*> o .: "age"

o .:? "nickname"    -- optional field → Maybe a
o .:! "nickname"    -- optional field, explicit null → Maybe a
p .!= "anon"        -- provide a default for a Maybe parser

Pretty-Printing

encodePretty  value          -- indented with defConfig (2-space indent)
encodePretty' config value   -- indented with custom Config

miso-aeson

If you prefer to use the aeson library directly, the miso-aeson package provides a compatibility shim that bridges aeson's ToJSON / FromJSON instances with miso's event decoder and fetch API, so existing aeson-derived instances can be used without rewriting them.

Styles

Miso does not prescribe a single CSS strategy. Three approaches work out of the box:

1. Structured DSL (Miso.CSS)

style_ takes a list of Style values (which are (MisoString, MisoString) pairs). Miso manages individual properties on the DOM node, merging and diffing them efficiently:

import qualified Miso.CSS as CSS
import           Miso.CSS.Color (RGB(..))

div_
  [ CSS.style_
      [ CSS.display "flex"
      , CSS.flexDirection "column"
      , CSS.backgroundColor (RGB 30 30 30)
      , CSS.color (RGB 255 255 255)
      ]
  ]
  []

Custom properties can be constructed with the =: operator (re-exported from Miso.Util):

"user-select" =: "none"

2. Inline string (styleInline_)

For simple or dynamic style strings, styleInline_ sets the element's style attribute as a raw string:

CSS.styleInline_ "display:flex; gap:8px; padding:16px"

3. External stylesheets

Link external CSS files from the <head> via the styles field on Component (see the Development section), or include them in your HTML template directly. This is the most common approach for production apps using Tailwind, Bootstrap, etc.

See miso-ui for a larger example.

Development

When developing miso applications interactively it is possible to append styles and scripts to the <head> portion of the page when the Component mounts. This is a convenience only meant to be used in development. We recommend guarding the usage behind a flag.

main :: IO ()
main = startApp defaultEvents counter
 where
   app = counter
#ifdef INTERACTIVE
     { scripts = [ Src "https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js" (False :: CacheBust) ]
     , styles = [ Href "https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" (False :: CacheBust)  ]
     }
#endif

See the miso-sampler repository for more information.

Debugging

Sometimes things can go wrong. Common errors like using onClick but not listening for the click event are common. These are errors that cannot be caught statically (unless we use a dependently-typed language like Idris). These can be detected by enabling DebugAll. Currently, debugging event delegation and page hydration is supported.

counter { logLevel = DebugAll }

Internals

Internally miso uses a global event queue and a scheduler to process all events raised by Component throughout the lifetime of an application. Events are processed in FIFO order, batched by the Component that raised them.

  • Event queue: All actions dispatched via a Sink (from event handlers, subscriptions, or io callbacks) are enqueued and drained by the scheduler.
  • Scheduler: The scheduler pulls actions off the queue one batch at a time, runs the update function for each, collects the resulting IO work, and executes it. Rendering (VDOM diff + patch) is triggered after each batch.
  • Waiter: A Waiter is a synchronization primitive used internally to coordinate the event loop — it blocks the scheduler thread until new work arrives, avoiding busy-waiting.
  • Event delegation: Rather than attaching listeners to individual DOM nodes, miso attaches a single capture and a single bubble listener to <body>. Incoming events are routed through the virtual DOM tree to the matching handler. This minimises listener churn when the VDOM is patched.
  • VDOM diffing: The diff algorithm in Miso.Diff compares old and new View trees and emits the minimal set of DOM mutations. Keyed children (see the Key section) significantly speed up child list reconciliation.

Prerendering

Prerendering is the process of delivering HTML from a web server before the client loads and performs any drawing to the page. In miso it comes in two flavors, static or dynamic prerendering. Static prerendering assumes no model state needs to be shared between the server and client. Dynamic uses hydrateModel to share model state.

Static prerendering

miso provides the prerender and miso functions to facilitate static prerendering. Any page can be generated from a miso View using the toHtml instance.

A simple example of static prerendering would be an index.html page with some HTML

echo "<html><head></head><body>hello world</body><html>" > index.html

And a miso application that looks like:

main :: IO ()
main = prerender defaultEvents $ (vcomp () noop $ \_ () -> "hello world") { logLevel = DebugPrerender }

Assuming the JS / WASM payload and index.html are delivered together from the web server, the console should output below

[DEBUG_HYDRATE] Successfully prerendered page

See the Haskell miso website console for an example usage of static prerendering with miso and miso-ui for prerender usage.

Dynamic prerendering

Dynamic prerendering shares model state between the server and client so the client can hydrate from a meaningful initial state rather than a blank model. The -fssr Cabal flag must be enabled when compiling the server.

The hydrateModel field on Component is Maybe (IO model). When set, the action runs once at hydration time to produce the initial model; it is ignored on subsequent remounts. A typical pattern embeds the model as JSON in the server response and reads it back on the client via the JS DSL:

myComp :: App Model Action
myComp = (vcomp defaultModel updateFn viewFn)
  { hydrateModel = Just $ do
      val <- jsg "window" ! "initialModel"
      fromJSValUnchecked val
  }

On the server, populate window.initialModel by embedding the JSON in a <script> tag alongside the rendered HTML:

serverView :: Model -> View Model Action
serverView m =
  div_ []
    [ script_ [] [ textRaw ("window.initialModel = " <> encode m) ]
    , appView m
    ]

When hydrateModel is Nothing, the static model field is used instead — equivalent to static prerendering.

Synopsis

API

Miso

miso Source #

Arguments

:: Eq model 
=> Events

Globally delegated Events

-> (URI -> App model action)

The Component application, with the current URI as an argument

-> IO () 

Runs an miso application.

Assumes the pre-rendered DOM is already present. Always mounts to <body>. Copies page into the virtual DOM.

main :: IO ()
main = miso defaultEvents app

prerender Source #

Arguments

:: Eq model 
=> Events

Globally delegated Events

-> App model action

Component application

-> IO () 

Like miso, except discards the URI argument.

Use this function if you'd like to prerender, but not use navigation.

main :: IO ()
main = prerender defaultEvents app

(🍜) Source #

Arguments

:: Eq model 
=> Events

Globally delegated Events

-> (URI -> App model action)

Component application, with the current URI as an argument

-> IO () 

Alias for miso.

App

type App model action = Component ROOT () model action Source #

A miso application is a top-level Component, which has no parent. This is enforced by specializing the parent type parameter to ROOT.

startApp Source #

Arguments

:: Eq model 
=> Events

Globally delegated Events

-> App model action

Component application

-> IO () 

Like miso, except it does not perform page hydration.

This function draws your application on an empty body

You will most likely want to use this function for your application unless you are using prerendering.

main :: IO ()
main = startApp defaultEvents app

renderApp Source #

Arguments

:: Eq model 
=> Events

Globally delegated Events

-> MisoString

Name of the JS object that contains the drawing context

-> App model action

Component application

-> IO () 

Runs a miso application, but with a custom rendering engine.

The MisoString specified here is the variable name of a globally-scoped JS object that implements the context interface per ts/miso/context/dom.ts This is necessary for native support.

It is expected to be run on an empty <body>

main :: IO ()
main = renderApp defaultEvents "my-context" app

Component

data Component parent props model action Source #

Application entry point

Constructors

Component 

Fields

  • model :: model

    Initial model

  • hydrateModel :: Maybe (IO model)

    Optional IO to load component model state, such as reading data from page. The resulting model is only used during initial hydration, not on remounts.

  • update :: action -> Effect parent props model action

    Updates model, optionally providing effects.

  • view :: props -> model -> View model action

    Draws View

  • subs :: [Sub action]

    Subscriptions to run during application lifetime

  • styles :: [CSS]

    CSS styles expressed as either a URL (Href) or as Style text. These styles are appended dynamically to the <head> section of your HTML page before the initial draw on <body> occurs.

    Since: 1.9.0.0

  • scripts :: [JS]

    JavaScript scripts expressed as either a URL (Src) or raw JS text. These scripts are appended dynamically to the <head> section of your HTML page before the initial draw on <body> occurs.

    Since: 1.9.0.0

  • mountPoint :: Maybe MountPoint

    ID of the root element for DOM diff. If Nothing is provided, the entire document body is used as a mount point.

  • logLevel :: LogLevel

    Debugging configuration for prerendering and event delegation

  • mailbox :: Value -> Maybe action

    Receives mail from other components

    Since: 1.9.0.0

  • bindings :: [Binding parent model]

    Data bindings between parent and child Components

    Since: 1.9.0.0

  • eventPropagation :: Bool

    Should events bubble up past the Component barrier.

    Defaults to False

    Since: 1.9.0.0

  • mount :: Maybe action

    action to execute during Component mount phase.

    Since: 1.9.0.0

  • unmount :: Maybe action

    action to execute during Component unmount phase.

    Since: 1.9.0.0

  • onPropsChanged :: Maybe (props -> props -> action)

    action to execute when Component props have changed (a.k.a. props phase). Receives previous props and current props as arguments.

    Since: 1.11.0.0

component Source #

Arguments

:: model

model

-> (action -> Effect parent props model action)

update

-> (props -> model -> View model action)

view

-> Component parent props model action 

Smart constructor for Component with sane defaults.

vcomp Source #

Arguments

:: model

model

-> (action -> Effect parent props model action)

update

-> (props -> model -> View model action)

view

-> Component parent props model action 

Synonym for component

(+>) infixr 0 Source #

Arguments

:: forall child childAction model action. Eq child 
=> MisoString

VComp key_

-> Component model () child childAction

Component

-> View model action 

Component mounting combinator

Used in the view function to mount a Component on any VNode.

"component-id" +> component model noop $ \m ->
  div_ [ id_ "foo" ] [ text (ms m) ]

Since: 1.9.0.0

mount_ Source #

Arguments

:: Eq child 
=> Component parent () child childAction

Component to mount

-> View parent action 

Component mounting combinator.

Note: only use this if you're certain you won't be diffing two Component against each other. Otherwise, you will need a key to distinguish between the two Component, to ensure unmounting and mounting occurs.

mount_ $ component model noop $ \m ->
 div_ [ id_ "foo" ] [ text (ms m) ]

Since: 1.9.0.0

View

vnode :: Namespace -> MisoString -> [Attribute action] -> [View model action] -> View model action Source #

Create a new VNode.

Synonym for node

vtext :: MisoString -> View model action Source #

Synonym for text

Sink

withSink Source #

Arguments

:: (Sink action -> IO ())

Callback function that provides access to the underlying Sink.

-> Effect parent props model action 

withSink allows users to write to the global event queue. This is useful for introducing IO into the system. A synonym for tell, specialized to Effect.

A use-case is scheduling an IO computation which creates a 3rd-party JS widget which has an associated callback. The callback can then call the sink to turn events into actions.

update FetchJSON = withSink $ \sink -> getJSON (sink . ReceivedJSON) (sink . HandleError)

Since: 1.9.0.0

type Sink action = action -> IO () Source #

Function to write to the global event queue for processing by the scheduler.

Mail

mail Source #

Arguments

:: ToJSON message 
=> ComponentId

ComponentId to receive mail

-> message

The message to send

-> IO () 

Send any ToJSON message => message to a Component mailbox, by ComponentId

io_ $ mail componentId ("test message" :: MisoString) :: Effect parent props model action

Since: 1.9.0.0

checkMail Source #

Arguments

:: FromJSON value 
=> (value -> action)

Successful callback

-> (MisoString -> action)

Errorful callback

-> Value

The message received to parse.

-> Maybe action 

Helper function for processing Mail from mail.

data Action
  = ParsedMail Message
  | ErrorMail MisoString

main :: IO ()
main = app { mailbox = checkMail ParsedMail ErrorMail }

Since: 1.9.0.0

parent Source #

Arguments

:: (parent -> action)

Successful callback

-> action

Errorful callback

-> Effect parent props model action 

Fetches the parent model from the child (if parent exists).

N.B. this is a no-op for ROOT.

Since: 1.9.0.0

mailParent Source #

Arguments

:: ToJSON message 
=> message

Message to send

-> Effect parent props model action 

Send any ToJSON message => message to the parent's Component mailbox

mailParent ("test message" :: MisoString) :: Effect parent props model action

Since: 1.9.0.0

mailChildren Source #

Arguments

:: ToJSON message 
=> message

Message to send

-> Effect parent props model action 

Send any ToJSON message => message to the children's Component mailbox

N.B. this is only relevant for immediate descendants (not all descendants).

mailChildren ("test message" :: MisoString) :: Effect parent props model action

Since: 1.9.0.0

mailDescendants Source #

Arguments

:: ToJSON message 
=> message

Message to send

-> Effect parent props model action 

Send any ToJSON message => message to all descendants Component mailbox

Unlike mailChildren, this is relevant for all descendants Component.

mailDescendants ("test message" :: MisoString) :: Effect parent props model action

Since: 1.12.0.0

mailAncestors Source #

Arguments

:: ToJSON message 
=> message

Message to send

-> Effect parent props model action 

Send any ToJSON message => message to all ancestor Component mailbox.

This function walks the Component ancestor hierarchy, delivering mail along the way.

mailAncestors ("test message" :: MisoString) :: Effect parent props model action

Since: 1.11.0.0

broadcast Source #

Arguments

:: (Eq model, ToJSON message) 
=> message

Message to broadcast to all other Component

-> Effect parent props model action 

Sends a message to all Component mailbox, excluding oneself.

update :: action -> Effect parent props model action
update _ = broadcast (String "public service announcement")

Since: 1.9.0.0

Subscriptions

startSub Source #

Arguments

:: ToMisoString subKey 
=> subKey

The key used to track the Sub

-> Sub action

The Sub

-> Effect parent props model action 

Starts a named Sub dynamically, during the life of a Component. The Sub can be stopped by calling Ord subKey => stop subKey from the update function. All Sub started will be stopped if a Component is unmounted.

data SubType = LoggerSub | TimerSub
  deriving (Eq, Ord)

update Action =
  startSub LoggerSub $ \sink -> forever (threadDelay (secs 1) >> consoleLog "test")

Since: 1.9.0.0

stopSub Source #

Arguments

:: ToMisoString subKey 
=> subKey

The key used to stop the Sub

-> Effect parent props model action 

Stops a named Sub dynamically, during the life of a Component. All Sub started will be stopped automatically if a Component is unmounted.

data SubType = LoggerSub | TimerSub
  deriving (Eq, Ord)

update Action = do
  stopSub LoggerSub

Since: 1.9.0.0

type Sub action = Sink action -> IO () Source #

Type synonym for constructing subscriptions.

For example usage see Miso.Subscription

The Sink function is used to write to the global event queue.

Effect

issue Source #

Arguments

:: action

action to raise

-> Effect parent props model action 

Issue a new action to be processed by update.

data Action = HelloWorld
type Model  = Int

update :: Action -> Effect parent Model Action
update = \case
  Click -> issue HelloWorld

Since: 1.9.0.0

batch Source #

Arguments

:: [IO action]

Batch of IO actions to execute

-> Effect parent props model action 

Smart constructor for an Effect with multiple IO actions.

Since: 1.9.0.0

io Source #

Arguments

:: IO action

IO action to execute asynchronously

-> Effect parent props model action 

Schedule a single IO action for later execution.

Note that multiple IO action can be scheduled using tell from the mtl library.

Since: 1.9.0.0

io_ Source #

Arguments

:: IO ()

IO action to execute asynchronously

-> Effect parent props model action 

Like io but doesn't cause an action to be dispatched to the update function.

This is handy for scheduling IO computations where you don't care about their results or when they complete.

Note: The result of IO a is discarded.

Since: 1.9.0.0

sync Source #

Arguments

:: IO action

IO action to execute synchronously

-> Effect parent props model action 

Schedule a single IO action, executed synchronously. For asynchronous execution, see io.

Please use this with caution because it will block the render thread.

Since: 1.9.0.0

sync_ Source #

Arguments

:: IO ()

IO action to execute synchronously

-> Effect parent props model action 

Like sync, except discards the result.

Since: 1.9.0.0

for Source #

Arguments

:: Foldable f 
=> IO (f action)

actions executed in batch.

-> Effect parent props model action 

Like io but generalized to any instance of Foldable

This is handy for scheduling IO computations that return a Maybe value

Since: 1.9.0.0

JS file embedding

withJS Source #

Arguments

:: IO a

IO action to execute in between evalFile

-> IO a 

Load miso's javascript.

You don't need to use this function if you're compiling w/ WASM and using miso or startApp. It's already invoked for you. This is a no-op w/ the JS backend.

If you need access to FFI to call functions from `miso.js`, but you're not using startApp or miso, you'll need to call this function (w/ WASM only).

Bindings

Primitives for synchronizing parent and child models.

DSL

A JavaScript DSL for easy FFI interoperability

module Miso.DSL

Effect

Effect, Sub, and Sink types for defining update functions and subscriptions.

Event

Functions for specifying component lifecycle events and event handlers.

module Miso.Event

Fetch

Interface to the Fetch API for making HTTP requests.

module Miso.Fetch

PubSub

Publish / Subscribe primitives for communication between components.

Property

Construct custom properties on DOM elements.

Reload

Support for clearing the page during live-reloading w/ WASM browser mode.

Subscriptions

Subscriptions for external events (mouse, keyboard, window, history, etc.).

Storage

Web Storage API (Local and Session storage) interface.

Types

Core types for Miso applications.

module Miso.Types

Util

Utility functions for views, parsing, and general purpose combinators.

module Miso.Util

FFI

Foreign Function Interface (FFI) utilities for interacting with JavaScript.

module Miso.FFI

State management

State management for Miso applications.

module Miso.State