html_of_jsx is an implementation of JSX designed to render HTML on the server, without React or anything else. It's a simple library that allows you to write HTML in a declarative way with the component model.

This library is extracted from server-reason-react and simplified to just work with HTML5.


opan pin add html_of_jsx ""
(libraries html_of_jsx)
(preprocess (pps html_of_jsx.ppx))


Jsx module that exposes some helpers to construct elements and Html_of_jsx to render them, the rest are functions with labelled arguments.


It's just HTML (no className, no htmlFor, etc.)

let element = <a href="">
  <span> {"Click me!"} </span>

> Note reserved keywords aren't possible as props. For example class => class_ or type => type_.

Components are functions with labeled arguments

let component = (~name, ()) => {
  <div> <h1> {Jsx.text("Hello, " ++ name ++ "!")} </h1> </div>;

Html_of_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.

Uppercase components default to make

module Button = {
  let make = () => {
    <button onclick="onClickHandler"> {Jsx.text("Click me")} </button>;

Html_of_jsx.render(<Button />);
// is equivalent to
Html_of_jsx.render(<Button.make />);

Brings the power of interleaving expressions (stolen from JSX)

let component = (~name, ~children, ()) => {
    <h1> {("Hello, " ++ name ++ "!") |> Jsx.text} </h1>
    <h2> children </h2>

  <component name="World"> {Jsx.text("This is a children!")} </component>,

List of childrens are available with Jsx.list

    {["This", "is", "an", "unordered", "list"]
     |> => <li> {Jsx.text(item)} </li>)
     |> Jsx.list}


HTML attributes are type-checked and only valid attributes are allowed

    <h1 noop=1> {Jsx.text("Hello, world!")} </h1>
// Error: prop 'noop' isn't valid on a 'h1' element.

    <h1 class_=1> {Jsx.text("Hello, world!")} </h1>
// Error: This expression has type int but an expression was expected of type string

And also friendly, it recommends you the correct attribute if you misspell it

          <div ?onClick />
// Error: prop 'onClick' isn't valid on a 'div' element.
//        Hint: Maybe you mean 'onclick'?


Only 2 function to learn, the rest are your functions (aka components):

The rest are helpers on Jsx.*, like ( Check the !Jsx if you are curious

Html_of_jsx.render(<h1>{Jsx.text("Hello, world!")}</h1>);

Supports children as list of elements

let component = (~name, ~children, ()) => {
    <h1> {Jsx.text("Hello, " ++ name ++ "!")} </h1>
    <h2> {children} </h2>

Html_of_jsx.render(<component> {"This is a children!"} </component>)

Supports fragments

let component: Jsx.element = <> <div class_="md:w-1/3" /> <div class_="md:w-2/3" /> </>;

Html_of_jsx.render(<component> {"This is a children!"} </component>)

Works with Reason

let component = (~name, ()) => {
  <div> <h1> {Jsx.text("Hello, " ++ name ++ "!")} </h1> </div>;

Html_of_jsx.render(<component name="World" />);

Works with mlx (

let component ~name =
    <h1> ("Hello, " ^ name ^ "!") </h1>

Html_of_jsx.render <component name="World" />