miso

Core concepts

Props

Inspired by React props, miso allows a parent component to pass read-only data down to a child via props. Props are synchronous: when they change in the parent, the child re-renders.

Props vs component-local state

  • model — component-local state, owned and mutated exclusively by the component through its update. No other component can write to it directly.
  • props — data inherited from the parent. Props flow downward and are read-only from the child's perspective; the parent decides what to pass at mount time.

This mirrors React's distinction between useState and the props a function component receives.

When to use props

Props suit metadata — contextual or configuration data the child needs to know about but should not own: a display name, a theme token, a locale, a read-only identifier. If the data drives the child's own business logic — counters it increments, form fields it edits, async state it manages — it belongs in the child's model. Prefer props for "what the child should know" and the model for "what the child should do".

Props in view and update

view always takes props as its second argument; top-level applications have no parent, so props are ():

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

Use getProps inside Effect (or Miso.Lens.view props) to read the current value:

update = \case
  SomeAction -> do
    p <- getProps
    io_ (consoleLog (ms (show p)))

Passing props to a child

Use mountWithProps_ (keyed) or mountWithProps (unkeyed) in the parent's view:

mountWithProps_
  :: (Eq context, Eq model, Eq props)
  => MisoString -> props
  -> Component context props model action
  -> View context parentModel parentAction

Example: child reading parent-supplied props

-- The props type: what the parent shares with the child
newtype Greeting = Greeting MisoString deriving (Eq)

--                  context props    model  action
child :: Component ()      Greeting ()     ChildAction
child = vcomp () updateChild viewChild
  where
    viewChild :: () -> Greeting -> () -> View () () ChildAction
    viewChild _ (Greeting g) _ = H.div_ [] [ text ("Hello, " <> g <> "!") ]

    updateChild :: ChildAction -> Effect () Greeting () ChildAction
    updateChild = \case
      ReadGreeting -> do
        Greeting g <- getProps
        io_ (consoleLog g)

-- Parent component: owns the greeting, passes it to the child as props
parentComp :: App ParentModel ParentAction
parentComp = 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
  • Props flow from parent to child explicitly via mountWithProps_; the child's context is the shared global context.
  • getProps inside the child's update yields a Greeting. The child only sees what the parent chose to share.
  • The root App always has context ~ () and props ~ (); no plumbing is needed for startApp.
  • The onPropsChanged hook dispatches an action with the previous and current props whenever they change.

Try it

Props flowing from a parent to a childlive

Hello, World!

props changed 0 times

-- The props type: what the parent shares with the child.
newtype Greeting = Greeting MisoString
  deriving (Show, Eq)

-- The parent owns the name and passes it down as props.
data NamerAction = NameChanged MisoString

namer :: Eq ctx => Component ctx () MisoString NamerAction
namer = component "World" update view
  where
    update (NameChanged s) = this .= s

    view _ () name =
      H.div_ []
        [ H.input_ [ HP.value_ name, HE.onInput NameChanged, HP.placeholder_ "Your name" ]
        , mountWithProps_ "greeter" (Greeting name) greeter   -- keyed, with props
        ]

-- The child reads its props in view and in update.
data GreeterAction = LogProps | PropsChanged Greeting Greeting

greeter :: Component ctx Greeting Int GreeterAction
greeter = (component 0 update view)
  { onPropsChanged = Just PropsChanged }      -- react when the parent changes props
  where
    update = \case
      PropsChanged _old _new -> this += 1
      LogProps -> do
        Greeting g <- getProps                -- props are readable in Effect
        io_ (consoleLog ("props are: " <> g))

    view _ (Greeting g) changes =
      H.div_ []
        [ H.p_ [] [ "Hello, ", H.strong_ [] [ text g ], "!" ]
        , H.p_ [ HP.class_ "muted" ] [ "props changed ", text (ms changes), " times" ]
        , H.button_ [ HE.onClick LogProps ] [ "log props to console" ]
        ]