Composable, bidirectional SPA routing for Elm 0.19.1. Define your routes once as a typed codec pipeline and get both URL parsing and URL building from the same definition - no code generation, no CLI, no magic. It is a library you wire up yourself, not a framework that owns your app structure.
module Main exposing (main)
import App
import Html exposing (a, div, h1, text)
import Html.Attributes exposing (href)
import Route exposing (Router)
import Url exposing (Url)
type Route
= Home
| Blog
| BlogPost Int
| NotFound
router : Router Route
router =
Route.oneOf
[ Route.top Home
, Route.codec Blog (\r -> case r of Blog -> Just () ; _ -> Nothing)
|> Route.literal "blog"
, Route.codec BlogPost (\r -> case r of BlogPost id -> Just ( id, () ) ; _ -> Nothing)
|> Route.literal "blog"
|> Route.int
]
toRoute : Url -> Route
toRoute url =
Route.fromUrl router url |> Maybe.withDefault NotFound
main =
App.create
{ router = router
, toRoute = toRoute
, toPage = \route _ ->
App.page
{ init = \_ -> ( (), Cmd.none )
, update = \_ m -> ( m, Cmd.none )
, view = \_ _ -> div [] [ h1 [] [ text (Debug.toString route) ] ]
, subscriptions = \_ _ -> Sub.none
}
, shared =
{ init = \_ -> ( (), Cmd.none )
, update = \_ s -> ( s, Cmd.none )
, subscriptions = \_ -> Sub.none
}
}Route.parse router "/blog/42" returns Just (BlogPost 42).
Route.toPath router (BlogPost 42) returns Just "/blog/42".
withQuery, parseFull, toFullPath, and a record-friendly succeed/with/build pipeline give you typed query params alongside path paramsint, string, float, uuid, nonEmptyString, plus splat for variable-length tails and custom for anything elseintRange min max, slug, and enum validate values at parse time and refuse to build URLs that would not round-tripliteralCi parses any case but builds the canonical form, useful for SEO redirect hygienenest "blog" [ ... ] groups routes under a common prefix and can be nested arbitrarilywithBasePath "/admin" mounts the entire router at a sub-pathparseHash/toHashPath/fromUrlHash for static hosts that cannot rewrite pathsstartsWith for highlighting nav links when the current route is under a sectioncanonicalize / redirectTo drive redirect-to-canonical handlers without users threading parse |> toPath themselvesQuery.required plus parseQueryStrict (returns Maybe) or parseQueryResult (returns Result (List String) a listing missing keys)Query.update, drop, toList, fromList, lookup, merge, preserve, except, canonicalString for query operations that don't fit the typed pipelineApp.page supports static, sandbox, and element-style pages via init/update/view/subscriptionsshared record threaded through page init, view, and subscriptions| Feature | elm-route-craft | elm-land | elm-spa | Vanilla Elm |
|---|---|---|---|---|
| Code generation required | No | Yes | Yes | No |
| CLI required | No | Yes | Yes | No |
| Bidirectional routing | Yes | No | No | Manual |
| Typed query parameters | Yes | Partial | No | Manual |
| Nested route prefixes | Yes | File-based | No | Manual |
| Page types (init/update/view/subs) | Yes | Yes | Yes | Yes |
| Shared state | Yes | Yes | Partial | Manual |
| Route guards / onEnter hooks | Yes | Partial | No | Manual |
| Composable layouts | Yes | Yes | No | Manual |
| Plain Elm, no framework lock-in | Yes | No | No | Yes |
splat, float, nonEmptyString, uuid, startsWith, the Query.succeed/with/build pipeline, and Query.withDefault.parseHash / toHashPath / fromUrlHash for static hosts that cannot rewrite paths.Route and Appelm install joshburgess/elm-route-craftBSD-3-Clause