Getting started
Your first Component
The core type of miso is Component. To define one, use the component smart constructor (or its synonym vcomp). Below is a simple counter.
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 global context
-- | * - The props inherited from the parent Component
-- | | * - The type of the current Component model
-- | | | * - The action that updates the model
-- | | | |
counter :: Component () () Int Action
counter = vcomp m u v
where
-- | Initial model value
m :: Int
m = 0
u :: Action -> Effect () () Int Action
u = \case
Add -> this += 1
Subtract -> this -= 1
v :: () -> () -> Int -> View () Int Action
v _context _props 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)data CounterAction = Add | Subtract
deriving (Show, Eq)
counter :: Component ctx () Int CounterAction
counter = component m u v
where
m = 0
u = \case
Add -> this += 1
Subtract -> this -= 1
v _ () n =
H.div_ [ HP.class_ "row" ]
[ H.button_ [ HE.onClick Subtract ] [ "−" ]
, H.strong_ [] [ text (ms n) ]
, H.button_ [ HE.onClick Add ] [ "+" ]
]Four type parameters follow every Component: the global context (shared by the whole tree), the props passed by the parent, the component's own model and the action type its update consumes. A top-level application fixes context and props to (); the App synonym spells that out:
type App model action = Component () () model action
startApp :: Eq model => Events -> App model action -> IO ()Running it
We recommend startApp as the starting point — it sets up event listeners, performs the initial draw and assumes <body> is empty. The miso function (and prerender) assume <body> has already been populated by the result of view: instead of drawing, miso hydrates. If the structures do not match it falls back to drawing from scratch.
main :: IO ()
main = miso defaultEvents $ \uri -> counter -- hydrate a prerendered pageDoing something on mount
It is possible to execute an initial action when a Component is first mounted with the mount hook (and, similarly, unmount):
data Action = Init | Add | Subtract
main :: IO ()
main = startApp defaultEvents counter { mount = Just Init }
update :: Action -> Effect () () Int Action
update = \case
Init -> io_ (consoleLog "hello world!")
...