Skip to content
Melact

Get started

From reason-react to Melact in a few minutes.

Melact is a library and a ppx for Melange. It keeps reason-react's modules, so you switch the libraries in dune and build.

Step 0

Requirements

You need OCaml ≥ 5.4, Melange ≥ 5.1, dune ≥ 3.16 and Node for the tests.

Melact is not on opam yet: the opam packages melhole, melact and melact-ppx are the next step of the roadmap. Until then, build it from github.com/jongleb/melact.

Step 1

Switch the libraries

Take a reason-react + Melange app and change two lines in its dune files:

dune
- (libraries reason-react)
+ (libraries melact)
- (preprocess (pps reason-react-ppx))
+ (preprocess (pps melact-ppx))

and get a build with no React in the bundle and no virtual DOM. Migrating lists what is supported.

Step 2

mlx, Reason or OCaml

melact-ppx takes JSX in mlx, Reason and OCaml [@JSX]. For .mlx files, the dialect goes intodune-project, as in Melact's own repository:

dune-project
(dialect
 (name mlx)
 (implementation
  (extension mlx)
  (merlin_reader mlx)
  (preprocess
   (run mlx-pp %{input-file}))))

Step 3

A component compiles to one template

A whole tree compiles to one template. This mlx counter is one <template> with six holes, the same as the hand-written one:

counter.mlx
let[@react.component] make ~initial () =
  let count, setCount = React.useState (fun () -> initial) in
  let tone = if count < 0 then "negative" else "" in
  <div className="counter">
    <button onClick=(fun _ -> setCount (fun n -> n - 1))>(React.string "-")</button>
    <output className=tone>(React.int count)</output>
    <button onClick=(fun _ -> setCount (fun n -> n + 1))>(React.string "+")</button>
    <button onClick=(fun _ -> setCount (fun _ -> initial)) disabled=(count = initial)>
      (React.string "reset")
    </button>
  </div>

ReactDOM.Client clones the templates into the element, so the code that mounts your app stays as it is. The example lives inexamples/counter-react.

Optional

Server rendering

ReactDOMServer.renderToString and ReactDOM.Client.hydrateRoot work as in React. The code that takes server HTML over costs about a fifth of a small app's bundle, so it is generated only with the ppx's -hydratable flag:

dune
(pps melact-ppx -hydratable)

Without it the server's HTML shows until the app mounts over it.

Explore

From the repository

Build the libraries and examples, run the dual test suite, and open the counter:

shell
$ dune build                          # libraries and examples
$ dune build @runtest-js && npx vitest run
 ✓ test/view/view_test.mjs (17 tests)
 ✓ test/view/reconcile_test.mjs (9 tests)
 ✓ test/react/hooks_test.mjs (15 tests)
 ✓ test/dual/react/dual_test.mjs (11 tests)
 ✓ test/dual/melact/dual_test.mjs (11 tests)
$ dune test test/ppx                  # melact-ppx expect tests
$ npx vite                            # then open http://localhost:5173/examples/counter/

vite.config.js resolves the bare imports Melange emits (melange.js/…) inside each _build/…/output directory, for vitest and for the examples alike.