The docs for JSX in Reason are incomplete. How do I use plain JSX in plain Reason, without any React externals. For example, simply dropping JSX into some Reason causes unfound module errors.
Vanilla JSX in Reason
The JSX transformation is explained here: https://reasonml.github.io/reason-react/docs/en/jsx
The sugared JSX version turns into unsugared function calls, referring to the modules React
and ReactDOMRe
. If you want to use it in your own projects, an easy way to do that is to create modules and functions with those exact names which do what you want them to.
My confusion is stemming from the statement in the Reason docs that Reason JSX is not Reason-React, specifically stating that the JSX is independent of any specific framework, and yet everything I find refers to React. Can I use JSX only with a framework?
As far as I know you can use JSX with any library or framework as long as provide the appropriate modules and functions that JSX desugars to, as I mentioned before.
For example,
<div foo={bar}> {child1} {child2} </div>
desugars to
ReactDOMRe.createDOMElementVariadic(
"div",
~props=ReactDOMRe.domProps(~foo=bar, ()),
[|child1, child2|]
);
So as long as you create a module ReactDOMRe
with a function createDOMElementVariadic
, the desugar will work fine and therefore JSX will work fine. Now what your version of ReactDOMRe.createDOMElementVariadic
does, is upto you. That’s how you can integrate JSX with any library or framework.
This is incorrect. What described here is ReasonReact’s JSX transformation, reason’s JSX transformation is difference. I’m on mobile right now so I can’t provide a full answer, But I’ll try to remember and edit this reply later when I got back to my PC
Can I use JSX only with a framework?
You can use JSX in non-reasonreact framework. More details here - https://reasonml.github.io/docs/en/jsx.
Vanilla JSX is transformed by a compiler into normal function calls, for example,
<div foo={bar}> child1 child2 </div>
is tranformed into
([@JSX] div(~foo=bar, ~children=[child1, child2], ()));
which is a normal function call syntax in Reason
. So if you want to use the above JSX in your normal reasonml native program, you will need to define a function called div
, like so
let div = (~firstname, ~lastname, ~children as _) => Printf.sprintf("Hello %s, %s!", lastname, firstname);
let () = print_string(<div firstname="foo" lastname="bar"/>);
The [@JSX]
is an annotation called attribute
in ocaml/reasonml. More details here (https://caml.inria.fr/pub/docs/manual-ocaml/manual035.html). This attribute([@JSX]) is used by ReasonReact
ppx(https://ocamlverse.github.io/content/ppx.html) to further transform function call syntax to reasonreact specific forms, i.e. from
<div className="hello" id="id1"/>;
to
((div ~first:"" ~className:(("hello")[@reason.raw_literal "hello"])
~id:(("id1")[@reason.raw_literal "id1"]) ~children:[] ())[@JSX ])
is done by the ReasonML compiler.
From
((div ~first:"" ~className:(("hello")[@reason.raw_literal "hello"])
~id:(("id1")[@reason.raw_literal "id1"]) ~children:[] ())[@JSX ])
to
ReactDOMRe.createDOMElementVariadic(
"div",
~props=ReactDOMRe.domProps(~className="hello", ~id="id1" ()),
[||]
);
is done by ReasonReact ppx(https://github.com/BuckleScript/bucklescript/blob/72c23b3378afe9cd8477e9a16fcae958039f9828/jscomp/syntax/reactjs_jsx_ppx.cppo.ml)
Finally, the javascript output from the last transformation is done by the bucklescript compiler.
// Generated by BUCKLESCRIPT VERSION 5.0.4, PLEASE EDIT WITH CARE
'use strict';
var React = require("react");
React.createElement("div", {
className: "hello",
id: "id1"
});
/* Not a pure module */
If you want to explore further on the JSX ppx mechanism, you can see my experiment here -https://github.com/bikallem/jsx-ssr/. Here I have transformed normal ReasonML JSX to render HTML in the server side without the use of nodejs.
This goes quite a ways towards explaining how things should work in one particular way and I appreciate it very much. I’m still confused because if I open up a Reason file and just paste, for example from the docs, this
let component = <div foo={bar}> child1 child2 </div>;
I get this error: Error: Unbound module ReactDOMRe
This means that, without any other input, the compiler is assuming something related to React. Is that correct?
@maarekj is right, the behavior of JSX is related to your bsconfig.json. Specifically, if your bsconfig has:
{
...
"reason": {
"react-jsx": 2 // or 3
}
}
then the ReasonReact-specific version of JSX will be used.
Thanks for the clarity. I was hoping to specifically avoid ReasonReact and instead deal with just Reason. It was difficult since everyone writing online is only talking about ReasonReact. I can appreciate why–it’s marketable–but for someone with eyes only on the language itself, it was frustrating.
If you want to use ReasonML/JSX without ReasonReact then Reason native is what you want.
To get started with ReasonML native, you might find these projects useful
https://github.com/esy/esy - a native/opam compatible package manager
https://github.com/ocaml/dune - an ocaml/reasonml build tool
https://github.com/esy/pesy - an overlay over esy tool. For beginners of esy.
Note: If you specifically want to use JSX with reason native, see my first posts on this thread. An example of using JSX in native setting is https://github.com/revery-ui/revery where JSX is used to create a view-engine like for native desktop applications. Revery is in turn used to create a fast vim-powered editor called onivim2 - https://github.com/onivim/oni2