html_of_jsx is a library and a ppx to write HTML declaratively in OCaml, Reason and mlx.
This library was extracted from server-reason-react and simplified to only work with HTML and SVG.
opam install html_of_jsxadd it to your dune file
(libraries html_of_jsx)
(preprocess (pps html_of_jsx.ppx))className, no htmlFor, no onChange, etc...), just plain HTML5htmx page)The library exposes a single module JSX with a minimal API:
JSX.string, JSX.int, JSX.null, JSX.list, JSX.unsafe) into a JSX.t treeJSX.render wich transforms a JSX.t tree into an HTML stringJSX.t trees, which are known as "components"Visit the JSX page for the complete interface documentation
We stick to the HTML standard, so no react idioms like className, no htmlFor, etc.
let element = <span> {JSX.string("Hello world!")} </span>;Note: reserved keywords aren't possible as props. For example: class => class_ or type => type_.
let component = (~name, ()) => {
<div> <h1> {JSX.string("Hello, " ++ name ++ "!")} </h1> </div>;
};
JSX.render(<component name="lola" />);> Note that the component function needs to have a last argument of type unit in order to work properly with labelled arguments. Explained on the OCaml manual: Functions with only labelled arguments, need a last non labelled argument to be able to be called as a non curried function.
This makes possible to compose any HTML and abstract those components away. ~children is a prop that recieves the nested elements from the component invocation.
let hero = (~children, ()) => {
<main class_="fancy-hero"> {children} </main>;
};
JSX.render(<hero> {JSX.string("Hello, world!")} </hero>);In this example, the hero component is a function that takes a children prop and returns a main element with the class fancy-hero. The children prop is the nested elements from the component invocation.
make functionmodule Button = {
let make = () => {
<button onclick="onClickHandler"> {JSX.string("Click me")} </button>;
};
};
JSX.render(<Button />);
// is equivalent to
JSX.render(<Button.make />);let component = (~name, ~children, ()) => {
<div>
<h1> {("Hello, " ++ name ++ "!") |> JSX.string} </h1>
<h2> children </h2>
</div>;
};
JSX.render(
<component name="World"> {JSX.string("This is a children!")} </component>,
);JSX.listA component can receive more than one children, which is the case for the ul element, but many other elements too. To make sure a list of elements is an element, use the JSX.list function.
JSX.render(
<ul>
{["This", "is", "an", "unordered", "list"]
|> List.map(item => <li> {JSX.string(item)} </li>)
|> JSX.list}
</ul>,
);There are other cases where you want to construct many JSX.element without wrapping them in a HTML element, that's what Fragment is for: <> ... </>.
let component: JSX.element =
<>
<div class_="md:w-1/3" />
<div class_="md:w-2/3" />
</>;
let output = JSX.render(<component/>);
/* <div class="md:w-1/3"></div><div class="md:w-2/3"></div> */HTML attributes are type-checked and only valid attributes are allowed. It also ensures that the value is correct.
<h1 noop=1> {JSX.string("Hello, world!")} </h1>
^^^
// Error: prop 'noop' is not valid on a 'h1' element. <h1 class_=1> {JSX.string("Hello, world!")} </h1>
^
// Error: This expression has type int but an expression was expected of type stringAnd also in case of a misspell, it recommends the closest attribute
<div ?onClick />
^^^^
// Error: prop 'onClick' is not valid on a 'div' element.
// Hint: Maybe you mean 'onclick'?let component = (~name, ()) => {
<div>
<h1> {JSX.string("Hello, " ++ name ++ "!")} </h1>
</div>;
};
JSX.render(<component name="World" />);mlx is an OCaml syntax dialect which adds JSX syntax expressions
let component ~name () =
<div>
<h1> ("Hello, " ^ name ^ "!") </h1>
</div>
JSX.render <component name="World" />You can enable extra attributes in the ppx, and we currently support: htmx and react, check the pages for more information: htmx and react.