The successor to Incr_dom, Bonsai is a front-end web framework following the Elm style of a functional user-interface API.
UI Components are implemented as purely functional state-machines, and are composible in multiple ways:
- Parallel: Components can be built out of sub-components, and these sub-components don’t know about one another.
- Serial: Components can compute values that are used downstream by other components.
Incrementalization inside the framework means that values don’t get recomputed until necessary. This applies to every value, not just the view.
(* An example of a small Bonsai component for creating counter widgets ("+" and "-" buttons allow you to increment and decrement a number). The state of each counter is housed in a shared [Map]. Adding a new counter is as simple as adding a value to the map. *) module Model = struct type t = int Int.Map.t [@@deriving sexp, equal] end module Action = struct type t = | New | Update of int * int [@@deriving sexp] end let default_model = Int.Map.empty let apply_action ~inject:_ ~schedule_event:_ model = function | Action.New -> Map.add_exn model ~key:(Map.length model) ~data:0 | Update (location, diff) -> Map.update model location ~f:(Option.value_map ~default:0 ~f:(( + ) diff)) ;; let component = let%sub state = Bonsai.state_machine0 [%here] (module Model) (module Action) ~default_model ~apply_action in return @@ let%map state, inject = state in let button text action = Node.button ~attr:(Attr.on_click (fun _ -> inject action)) [ Node.text text ] in let add_button = button "add" New in let for_each i c = Node.div [ button "-1" (Update (i, -1)); Node.textf "%d" c; button "+1" (Update (i, 1)) ] in let counters = state |> Map.data |> List.mapi ~f:for_each in Node.div (add_button :: counters) ;;