A declarative DSL for building Elm applications as a reactive graph of cells, sources, and reactions — no hand-written Msg types, update branches, or subscriptions.
The runtime interprets your graph into a standard Elm Program. Cells hold state, sources are read-only views of state, and reactions turn cell changes into effects. Codecs travel with cells so persistence is structural.
Status: all layers of the design doc are shipped — cells, sources, actions, reactions, HTTP, debouncing, validation, forms, components, persistence. See the design doc's Future Work section for what's beyond the current scope (ListOf, routing, animation).
An input bound to a cell; a watch that re-renders whenever the cell changes. No Msg, no update.
module L01E02_Greeting exposing (main)
import Rad exposing (AppDef, AppModel, Cell, build, run, stringCodec, toSource, with)
import Rad.Internal.Engine exposing (Msg)
import SimpleView exposing (SimpleView, col, input, simpleViewEngine, text, watch)
type alias Model =
{ name : Cell String }
app : AppDef (SimpleView Model) Model {}
app =
{ init = build Model |> with "name" "" stringCodec
, computed = \_ -> {}
, view =
\model _ ->
col
[ input { label = "Name", cell = model.name }
, watch (toSource model.name) (\n -> text ("Hello, " ++ n ++ "!"))
]
, reactions = \_ _ -> []
}
main : Program () (AppModel Model) (Msg Model)
main =
run simpleViewEngine appA DebouncedCell exposes two sources: raw (every keystroke) and settled (after the debounce fires). A reaction watches settled, issues an HTTP request, and writes the Remote result into another cell. Latest-wins is enforced by the runtime.
module L03E02_SearchDebounced exposing (main)
import Json.Decode as Decode
import Rad
exposing
( AppDef, AppModel, Cell, DebouncedCell, Remote(..)
, build, listCodec, on, remoteCodec, run, stringCodec
, with, withDebounced
)
import Rad.Internal.Engine exposing (Msg)
import Rad.Http as Http exposing (RequestError, prodHandler, requestErrorCodec)
import Rad.View exposing (CommitTrigger(..))
import SimpleView exposing (SimpleView, col, debouncedInput, simpleViewEngine, text, watch)
type alias Model =
{ query : DebouncedCell String
, results : Cell (Remote RequestError (List String))
}
app : AppDef (SimpleView Model) Model {}
app =
{ init =
build Model
|> withDebounced "query" 500 "" stringCodec
|> with "results" Idle (remoteCodec requestErrorCodec (listCodec stringCodec))
, computed = \_ -> {}
, view =
\model _ ->
col
[ debouncedInput
{ label = "Search"
, cell = model.query
, triggers = [ OnEnter, OnBlur, OnTimeout ]
}
, watch (Rad.settled model.query) (\q -> text ("settled: \"" ++ q ++ "\""))
, watch (Rad.toSource model.results) renderResults
]
, reactions =
\model _ ->
[ on (Rad.settled model.query)
(\q ->
if String.trim q == "" then
Rad.noRequest
else
Http.httpGet prodHandler ("/api/search?q=" ++ q) matchesDecoder
)
model.results
]
}
matchesDecoder : Decode.Decoder (List String)
matchesDecoder =
Decode.field "matches" (Decode.list Decode.string)
renderResults : Remote RequestError (List String) -> SimpleView Model
renderResults r =
case r of
Idle -> text "(type to search)"
Loading -> text "searching…"
Failed _ -> text "(error)"
Done ms -> col (List.map (\m -> text (" • " ++ m)) ms)
main : Program () (AppModel Model) (Msg Model)
main =
run simpleViewEngine appcd examples
npm install
npm run devSee examples/ for the full set: counter, swap, derived reads, custom triggers, validated inputs, username-available check, component instances, tag pickers, and more.
Rad— public API:build,with,withDebounced,withValidated,on,run, codecs, actions, components,PersistConfig/persistNow.Rad.Form— orthogonal form layer: snapshot/dirty/reset, submit gating with typed clean values, status helpers.Rad.Internal.Engine—Msg,ViewEngine, and the glue for custom view backends.Rad.View— an HTML view engine and reactive primitives.Rad.Read— theReadmonad for computed values.Rad.Http— HTTP request builders that plug into reactions.
The full design rationale and layer-by-layer spec lives in docs/design-elm-rad.md. Implementation plans per layer are in docs/plans/.
MIT.