miso

Platform

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. This site's own routing is written with it.

Defining a Router with Generics

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

{-# LANGUAGE DeriveGeneric, DeriveAnyClass #-}
import GHC.Generics
import Miso.Router

data Route
  = Index                                                        -- "/"
  | About                                                        -- "/about"
  | Product (Capture "id" Int) (QueryParam "tab" MisoString)     -- "/product/42?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

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 update:

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

update = \case
  HandleRoute (Right Index) -> page .= HomePage
  HandleRoute (Right About) -> page .= AboutPage
  HandleRoute (Left _)      -> page .= NotFound

uriSub is the lower-level variant — it delivers the raw URI without parsing.

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

href_ (from Miso.Router) produces a type-safe href from any route. Pair it with onClickPrevent to navigate client-side while keeping a real link for the browser and crawlers:

H.a_ [ href_ (Product (Capture 10) (QueryParam Nothing))
     , onClickPrevent (Go (Product (Capture 10) (QueryParam Nothing))) ]
     [ "Go to product 10" ]