| Copyright | (C) 2016-2026 David M. Johnson (@dmjio) |
|---|---|
| License | BSD3-style (see the file LICENSE) |
| Maintainer | David M. Johnson <code@dmj.io> |
| Stability | experimental |
| Portability | non-portable |
| Safe Haskell | None |
| Language | Haskell2010 |
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:
misouses a Virtual DOM with a diffing algorithm that is responsible for all DOM modification andComponentlifecycle 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. Internallymisovirtualizes both thecaptureandbubblephases 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.
misoprovides its own HTML rendering (Miso.Html.Render) to render HTML on the server and themisofunction exists on the client to "hydrate" the virtual DOM with the DOM. - Components: A
Componentcan be considered an instance of amisoapplication. AComponentcontains user-defined state, logic for updating this state, and a function for creating UI templates from this user-defined state.Componentcan nest otherComponent, 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:
Componentexposemountandunmountlifecycle hooks. This allows users to define custom logic that will execute when aComponentmounts or unmounts.onCreatedandonDestroyedareVNodespecific lifecycle hooks. These hooks are commonly used forComponentcommunication and for third-party integration with JavaScript libraries. - State management:
Componentmodelstate 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.
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
Eqconstraint is required. We recommend using the default derivedEqinstance. - view:
This is the templating function that is used to construct a new virtual DOM (or HTML if rendering on the server).view:: props -> model ->Viewmodel action - update:
Theupdate:: action ->Effectparent props model actionupdatefunction handles how themodelevolves over time in response to events that are raised by the application. This function takes anyaction, updating themodeland optionally introducesIOinto 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 =startAppdefaultEventscounter
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 =startAppdefaultEventscounter {mount= Just Init } update ::Appmodel Action update = \case Init ->io_(consoleLog"hello world!")
Note also the signature of startApp.
startApp::Eqmodel =>Events->Appmodel action -> IO ()
The App type synonym is defined as:
typeAppmodel action =ComponentROOT() 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.
dataViewmodel action =VNodeNamespaceTag[Attributeaction] [Viewmodel action] |VText(MaybeKey)MisoString|VComp(MaybeKey) (SomeComponentmodel) |VFrag(MaybeKey) [Viewmodel 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.
dataSomeComponentparent = forall model action props . (Eq model, Eq props) =>SomeComponentprops (Componentparent props model action)
The smart constructors:
node,vnode— build aVNodetext,vtext— build aVTextcomponent,vcomp— build aVComp(vcompis a synonym forcomponent)fragment,vfrag,fragment_,vfrag_— build aVFrag- (
+>) — key and mount a childComponent
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->Componentmodel () child action ->Viewmodel a key+>vcomp =VComp(Just (toKey key)) (SomeComponent() vcomp)
Practically, using this combinator looks like:
view :: props -> Int ->ViewInt 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:
VNodeHTML"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_[onClickDoSomething ] [ "Click me" ]h1_[className"title" ] [text(ms pageTitle) ]
For elements not covered by Miso.Html.Element, use node (or its synonym vnode) directly:
nodeHTML"details" [] [nodeHTML"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:
node— raw constructor, takesNamespace, tag, attributes, childrenvnode— synonym fornode- All combinators in Miso.Html.Element, Miso.Svg.Element, Miso.Mathml.Element
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 .
String literals inside a child list are automatically promoted to View model actionVText 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: <b>bold</b>
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 worlddiv_[] [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 ->Viewmodel 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!" ::Viewmodel action)
The smart constructors for VText are:
text— single string, HTML-encoded on the servervtext— synonym fortexttextRaw— single string, never HTML-encodedtext_— list of strings joined with a spacetextKey— single keyed stringtextKey_— list of keyed strings joined with a spacekeyed— attach a key to anyView, includingVText
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 elementfragment[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/remountvfrag_"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 () 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.fragment []
The smart constructors for VFrag are:
fragment— unkeyed fragmentvfrag— unkeyed fragment (alias)fragment_— keyed fragmentvfrag_— 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::EventstouchEvents= 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) ->AttributeactiononChangeWith=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.
dataDecodera =Decoder{decoder::Value->Parsera ,decodeAt::DecodeTarget} -- | Example of a customDecoderfor thevalueproperty of an event target.valueDecoder::DecoderMisoStringvalueDecoder= 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:
dataAttributeaction =PropertyMisoStringValue-- ^ DOM property (key/value) |ClassList[MisoString] -- ^ CSS class list |On(Sinkaction -> ...) -- ^ Event handler |Styles(MapMisoStringMisoString) -- ^ 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 ,onClickMyAction -- 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 = 42textProp"placeholder" "Search…" -- sets element.placeholderboolProp"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 section for details.Key
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.
typeEffectparent props model action =RWS(ComponentInfoparent props) [Scheduleaction] model ()
- The
Readerportion ofEffectisComponentInfo.ask,asks,viewcan be used to access its fields. - The
Writerportion ofEffectis used to scheduleIOactions.tellcan be used to create aSchedulefor anIOaction that is executed according toSynchronicity. See alsowithSinkfor usage. - The
Stateportion ofEffectis used to manipulate themodel.get,put,modify, and theMonadStatelenses inLenscan be used to modify themodel.
IO can be performed either synchronously or asynchronously. By default all IO is asynchronous
Asynchronous IO
withSink: The core function (from which most other combinators are defined) that gives users access to the underlying eventSink. This also allows us to introduceIOinto the system. Themisoscheduler attaches exception handlers to allIOactions.- For maximum flexibility, the
MonadWriterinstance (tell) can be used to scheduleIO(see thewithSinkimplementation).
Synchronous IO
sync: Forces the scheduler to evaluateIOsynchronously. It is recommended to use theiofunction by default,sync*will* block the scheduler.
Sink
typeSinkaction = 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 — synchronous, parent-to-child read-only data passed at mount time (see below).
- Async mailbox — message-passing via
mailbroadcastcheckMail; anyComponentcan send aValueto any other byComponentId. - PubSub (Miso.PubSub) — publish/subscribe for fan-out messaging across unrelated
Component.
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
Componentitself through itsupdatefunction. No otherComponentcan write to it directly.
- props: Data inherited from the
parentComponent. 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 ->Effectparent props Model Action update = \case SomeAction -> do p <-getPropsio_(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 ():
typeAppmodel action =ComponentROOT() 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_:: (Eqchild,Eqprops) =>MisoString-> props ->Componentparent props child action ->Viewparent 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 = GreetingMisoStringderiving (Eq) ----------------------------------------------------------------------------- -- Child component -- -- parent props model action -- | | | | child ::ComponentParentModel Greeting () ChildAction child =vcomp() updateChild viewChild where viewChild :: Greeting -> () ->View() ChildAction viewChild (Greeting g) _ =div_[] [text("Hello, " <> g <> "!") ] updateChild :: ChildAction ->EffectParentModel Greeting () ChildAction updateChild = \case ReadGreeting -> do Greeting g <-getPropsio_(consoleLogg) ----------------------------------------------------------------------------- -- Parent component: owns the greeting, passes it to the child as props parent ::AppParentModel ParentAction parent =vcomp(ParentModel World)noopviewParent where viewParent :: () -> ParentModel ->ViewParentModel ParentAction viewParent _ (ParentModel g) =mountWithProps_"child" (Greeting g) child ----------------------------------------------------------------------------- newtype ParentModel = ParentModelMisoStringderiving (Eq) data ChildAction = ReadGreeting data ParentAction
A few things to notice:
- The child's
parenttype parameter isParentModel. This must match the parentComponentmodeltype —mountWithProps_enforces this at compile time. getPropsinside the child'supdateyields aGreeting, not the fullParentModel. The child only sees what the parent explicitly chose to share.- The root
Appalways hasprops ~ (); no extra plumbing is needed when callingstartApp.
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
mailcomponentId msg— send to a specificComponentId(obtained viaaskinsideEffect)mailParentmsg— send to the direct parentmailChildrenmsg— send to all immediate childrenmailAncestorsmsg— walk up the hierarchy, delivering to every ancestormailDescendantsmsg— walk down the hierarchy, delivering to every descendantbroadcastmsg— deliver to every mountedComponentexcept the sender
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 ::Componentparent props model Action myComp = (vcompm u v) {mailbox=checkMailReceivedMsg 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 =startAppdefaultEventsapp {subs= [ timerSub ] } timerSub ::SubAction timerSub sink =forever$ (threadDelay100000) >> 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) ->SubactiononLineSubf sink =createSubacquire release sink where release (cb1, cb2) = dowindowRemoveEventListener"online" cb1windowRemoveEventListener"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 ::SubAction timerSub sink =forever$ (threadDelay100000) >> 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
viewl -- read a field (MonadReader)setl v -- write a fieldoverl 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/makeClassysplice 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/HasLensderive lenses at compile time usingGHC.Generics— no TH splice required. RequiresTypeApplicationsand, optionally,OverloadedLabelsfor the#fieldshorthand.
{-# 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
Lensdirectly usinglensand thesynonym.Lenss a
name ::LensPersonMisoStringname =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 = doclearRect(0, 0, 800, 480)fillStyle(RGB30 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.
classToHtmla wheretoHtml:: a ->ByteString
Instances are provided for and View m a[:View m a]
import Miso.Html.Render (toHtml) pageHtml ::ByteStringpageHtml =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:objreads!"key"obj.key(#)— method call:objcalls#"method" argsobj.method(args)jsg— access a global JS variable by namejsgf— 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 reversible — prettyRoute 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",toCapturen ]
Subscribing to URI changes
routerSub listens to popstate events and delivers
the parsed route (or a RoutingError) to your update function:
app = (vcompm u v) {subs= [routerSubHandleRoute ] } 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
pushURIuri -- push a rawURIonto the History stackpushRouteroute -- push a typed route (serialised viaRouter)replaceURIuri -- replace the current history entryback-- go back one entryforward-- 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:
MisoStringisJSString, a direct reference to a JavaScript string — no marshalling cost when passing to the DOM or FFI. - Server / vanilla GHC (
ssrflag):MisoStringisText.
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:
dataValue=NumberDouble|BoolBool|StringMisoString|Array[Value] |ObjectObject|Null
Encoding
Encode any ToJSON instance to a MisoString:
encodevalue -- uses JS runtime on client, pure on serverencodePurevalue -- always uses pure Haskell implementation
Decoding
decodes :: Maybe a -- returns Nothing on failureeitherDecodes :: 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 =genericToJSONdefaultOptions{fieldLabelModifier=camelTo2'_' }
Building and Parsing Objects
-- Buildobject[ "name".=ms Alice, "age".=(30 :: Int) ] -- Parse (inside awithObjectcallback or event decoder)withObjectUser $ \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
encodePrettyvalue -- indented withdefConfig(2-space indent)encodePretty'config value -- indented with customConfig
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 values (which are Style(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 =startAppdefaultEventscounter 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, oriocallbacks) are enqueued and drained by the scheduler. - Scheduler: The scheduler pulls actions off the queue one batch at a time,
runs the
updatefunction for each, collects the resultingIOwork, and executes it. Rendering (VDOM diff + patch) is triggered after each batch. Waiter: AWaiteris 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
Viewtrees and emits the minimal set of DOM mutations. Keyed children (see theKeysection) 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 =prerenderdefaultEvents$ (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 ::AppModel Action myComp = (vcompdefaultModel updateFn viewFn) {hydrateModel= Just $ do val <-jsg"window"!"initialModel"fromJSValUncheckedval }
On the server, populate window.initialModel by embedding the JSON
in a <script> tag alongside the rendered HTML:
serverView :: Model ->ViewModel Action serverView m =div_[] [script_[] [textRaw("window.initialModel = " <>encodem) ] , appView m ]
When hydrateModel is Nothing, the static model field is used instead —
equivalent to static prerendering.
Synopsis
- miso :: Eq model => Events -> (URI -> App model action) -> IO ()
- prerender :: Eq model => Events -> App model action -> IO ()
- (🍜) :: Eq model => Events -> (URI -> App model action) -> IO ()
- type App model action = Component ROOT () model action
- startApp :: Eq model => Events -> App model action -> IO ()
- renderApp :: Eq model => Events -> MisoString -> App model action -> IO ()
- data Component parent props model action = Component {
- model :: model
- hydrateModel :: Maybe (IO model)
- update :: action -> Effect parent props model action
- view :: props -> model -> View model action
- subs :: [Sub action]
- styles :: [CSS]
- scripts :: [JS]
- mountPoint :: Maybe MountPoint
- logLevel :: LogLevel
- mailbox :: Value -> Maybe action
- bindings :: [Binding parent model]
- eventPropagation :: Bool
- mount :: Maybe action
- unmount :: Maybe action
- onPropsChanged :: Maybe (props -> props -> action)
- component :: model -> (action -> Effect parent props model action) -> (props -> model -> View model action) -> Component parent props model action
- vcomp :: model -> (action -> Effect parent props model action) -> (props -> model -> View model action) -> Component parent props model action
- (+>) :: forall child childAction model action. Eq child => MisoString -> Component model () child childAction -> View model action
- mount_ :: Eq child => Component parent () child childAction -> View parent action
- vnode :: Namespace -> MisoString -> [Attribute action] -> [View model action] -> View model action
- vtext :: MisoString -> View model action
- withSink :: (Sink action -> IO ()) -> Effect parent props model action
- type Sink action = action -> IO ()
- mail :: ToJSON message => ComponentId -> message -> IO ()
- checkMail :: FromJSON value => (value -> action) -> (MisoString -> action) -> Value -> Maybe action
- parent :: (parent -> action) -> action -> Effect parent props model action
- mailParent :: ToJSON message => message -> Effect parent props model action
- mailChildren :: ToJSON message => message -> Effect parent props model action
- mailDescendants :: ToJSON message => message -> Effect parent props model action
- mailAncestors :: ToJSON message => message -> Effect parent props model action
- broadcast :: (Eq model, ToJSON message) => message -> Effect parent props model action
- startSub :: ToMisoString subKey => subKey -> Sub action -> Effect parent props model action
- stopSub :: ToMisoString subKey => subKey -> Effect parent props model action
- type Sub action = Sink action -> IO ()
- issue :: action -> Effect parent props model action
- batch :: [IO action] -> Effect parent props model action
- io :: IO action -> Effect parent props model action
- io_ :: IO () -> Effect parent props model action
- sync :: IO action -> Effect parent props model action
- sync_ :: IO () -> Effect parent props model action
- for :: Foldable f => IO (f action) -> Effect parent props model action
- withJS :: IO a -> IO a
- module Miso.Binding
- module Miso.DSL
- module Miso.Effect
- module Miso.Event
- module Miso.Fetch
- module Miso.PubSub
- module Miso.Property
- module Miso.Reload
- module Miso.Subscription
- module Miso.Storage
- module Miso.Types
- module Miso.Util
- module Miso.FFI
- module Miso.State
API
Miso
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 =misodefaultEventsapp
Arguments
| :: Eq model | |
| => Events | Globally delegated |
| -> App model action |
|
| -> IO () |
Like miso, except discards the URI argument.
Use this function if you'd like to prerender, but not use navigation.
main ::IO() main =prerenderdefaultEventsapp
Arguments
| :: Eq model | |
| => Events | Globally delegated |
| -> (URI -> App model action) |
|
| -> IO () |
Alias for miso.
App
Arguments
| :: Eq model | |
| => Events | Globally delegated |
| -> App model action |
|
| -> 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 =startAppdefaultEventsapp
Arguments
| :: Eq model | |
| => Events | Globally delegated |
| -> MisoString | Name of the JS object that contains the drawing context |
| -> App model action |
|
| -> 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 =renderAppdefaultEvents"my-context" app
Component
data Component parent props model action Source #
Application entry point
Constructors
| Component | |
Fields
| |
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.
Arguments
| :: model | model |
| -> (action -> Effect parent props model action) | update |
| -> (props -> model -> View model action) | view |
| -> Component parent props model action |
Synonym for component
Arguments
| :: forall child childAction model action. Eq child | |
| => MisoString |
|
| -> Component model () child childAction | |
| -> View model action |
Arguments
| :: Eq child | |
| => Component parent () child childAction |
|
| -> 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 #
Sink
Arguments
| :: (Sink action -> IO ()) | Callback function that provides access to the underlying |
| -> 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.
updateFetchJSON =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.
Arguments
| :: ToJSON message | |
| => ComponentId |
|
| -> 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
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
Arguments
| :: (parent -> action) | Successful callback |
| -> action | Errorful callback |
| -> 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
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
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
Subscriptions
Arguments
| :: ToMisoString subKey | |
| => subKey | The key used to track the |
| -> Sub action | The |
| -> 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
Arguments
| :: ToMisoString subKey | |
| => subKey | The key used to stop the |
| -> Effect parent props model action |
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
Arguments
| :: action |
|
| -> 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
Like sync, except discards the result.
Since: 1.9.0.0
JS file embedding
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.
module Miso.Binding
DSL
A JavaScript DSL for easy FFI interoperability
module Miso.DSL
Effect
module Miso.Effect
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.
module Miso.PubSub
Property
Construct custom properties on DOM elements.
module Miso.Property
Reload
Support for clearing the page during live-reloading w/ WASM browser mode.
module Miso.Reload
Subscriptions
Subscriptions for external events (mouse, keyboard, window, history, etc.).
module Miso.Subscription
Storage
Web Storage API (Local and Session storage) interface.
module Miso.Storage
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