Counter 
We’re going build the classic frontend starter app, the counter, using ReasonReact. If you’ve already installed the starter project, you can run the project now:
- Go to the root directory of your melange-for-react-devs-template project
- Run npm run watchto start the Melange compiler in watch mode.
- In another terminal window, start the Vite dev server by running npm run serve. As a side effect, it will open a browser tab pointed to http://localhost:5174/.
The App component 
Open Index.re and you’ll see this:
module App = {
  [@react.component]
  let make = () => <div> {React.string("welcome to my app")} </div>;
};module App = {
  [@react.component]
  let make = () => <div> {React.string("welcome to my app")} </div>;
};This is just about the simplest component you can make, but through it, we can start to see some of the key differences of developing with ReasonReact:
- All components must be inside a module
- The makefunction renders the component and returnsReact.element
- We must decorate the makefunction with[@react.component]
- In JSX, we must wrap strings with calls to React.string
Let’s go over these differences in more detail.
Components are modules 
In the example above, a new module named App is defined. OCaml’s modules are somewhat like JavaScript modules, with one notable difference being that there can be multiples modules inside a single file. For now, you just need to know that all components in ReasonReact are modules, and the name of the component comes from the name of the module.
The make function 
The make function has the type unit => React.element, meaning it takes () as the only argument and returns an object of type React.element. You’ll need to decorate make with the attribute@react.component. We’ll go into the details later, but for now let’s just say that @react.component is there to reduce boilerplate and make our code more readable and easier to maintain.
Wrap strings with React.string 
Unlike in normal React, we must wrap strings inside function calls to convert them to the React.element type. This is exactly what the React.string function does—if you hover over it, you’ll see that it displays the type string => React.element.
Using the App component 
A little bit further down, we make use of the App component:
let node = ReactDOM.querySelector("#root");
switch (node) {
| None =>
  Js.Console.error("Failed to start React: couldn't find the #root element")
| Some(root) =>
  let root = ReactDOM.Client.createRoot(root);
  ReactDOM.Client.render(root, <App />);
};let node = ReactDOM.querySelector("#root");
switch (node) {
| None =>
  Js.Console.error("Failed to start React: couldn't find the #root element")
| Some(root) =>
  let root = ReactDOM.Client.createRoot(root);
  ReactDOM.Client.render(root, <App />);
};React.querySelector("#root") returns an option(Dom.element), meaning that if it doesn’t find the element, it returns None, and if it does find the element, it returns Some(Dom.element), i.e. the element wrapped in the Some constructor. The switch expression[1] allows you to succinctly express:
- If nodeisNone, log an error message
- Otherwise if nodeisSome(Dom.element), render theAppcomponent to the DOM
We’ll talk more about option in the Celsius converter chapter.
The Counter component 
Let’s create a counter component by creating a new file Counter.re with the following contents:
[@react.component]
let make = () => {
  let (counter, setCounter) = React.useState(() => 0);
  <div>
    <button onClick={_evt => setCounter(v => v - 1)}>
      {React.string("-")}
    </button>
    {React.string(Int.to_string(counter))}
    <button onClick={_evt => setCounter(v => v + 1)}>
      {React.string("+")}
    </button>
  </div>;
};[@react.component]
let make = () => {
  let (counter, setCounter) = React.useState(() => 0);
  <div>
    <button onClick={_evt => setCounter(v => v - 1)}>
      {React.string("-")}
    </button>
    {React.string(Int.to_string(counter))}
    <button onClick={_evt => setCounter(v => v + 1)}>
      {React.string("+")}
    </button>
  </div>;
};This is a component with a single useState hook. It should look fairly familiar if you know about hooks in React. Note that we didn’t need to manually define a module for Counter, because all source files in OCaml are automatically modules, with the name of the module being the same as the name of the file.
Now let’s modify App so that it uses our new Counter component:
module App = {
  [@react.component]
  let make = () => <Counter />;
};module App = {
  [@react.component]
  let make = () => <Counter />;
};The pipe last operator 
To display the number of the counter, we wrote {React.string(Int.to_string(counter))}, which converts an integer to a string, and then converts that string to React.element. In OCaml, there’s a way to apply a sequence of operations over some data so that it can be read from left to right:
{counter |> Int.to_string |> React.string}{counter |> Int.to_string |> React.string}This uses the pipe last operator, which is useful for chaining function calls.
Basic styling 
Let’s add a bit of styling to the root element of Counter:
<div
  style={ReactDOM.Style.make(
    ~padding="1em",
    ~display="flex",
    ~gridGap="1em",
    (),
  )}>
  <button onClick={_evt => setCounter(v => v - 1)}>
    {React.string("-")}
  </button>
  <span> {counter |> Int.to_string |> React.string} </span>
  <button onClick={_evt => setCounter(v => v + 1)}>
    {React.string("+")}
  </button>
</div>;<div
  style={ReactDOM.Style.make(
    ~padding="1em",
    ~display="flex",
    ~gridGap="1em",
    (),
  )}>
  <button onClick={_evt => setCounter(v => v - 1)}>
    {React.string("-")}
  </button>
  <span> {counter |> Int.to_string |> React.string} </span>
  <button onClick={_evt => setCounter(v => v + 1)}>
    {React.string("+")}
  </button>
</div>;Unlike in React, the style prop in ReasonReact doesn’t take a generic object, instead it takes an object of type ReactDOM.style that is created by calling ReactDOM.Style.make. This isn’t a sustainable way to style our app—later, we’ll see how to style using CSS classes.
Congratulations! You’ve created your first ReasonReact app and component. In future chapters we’ll create more complex and interesting components.
Overview 
- How to create and run a basic ReasonReact app
- ReasonReact components are also modules
- OCaml has an optiontype whose value can be eitherNoneorSome(_)
- The pipe last operator (|>) is an alternate way to invoke functions that enables easy chaining of function calls
- The styleprop doesn’t take generic objects
Exercises 
1. What happens if you try to remove the | None branch of the switch (node) expression in Index.re?
switch (node) {
| None => 
  Js.Console.error("Failed to start React: couldn't find the #root element") 
| Some(root) =>
  let root = ReactDOM.Client.createRoot(root);
  ReactDOM.Client.render(root, <App />);
};switch (node) {
| None => 
  Js.Console.error("Failed to start React: couldn't find the #root element") 
| Some(root) =>
  let root = ReactDOM.Client.createRoot(root);
  ReactDOM.Client.render(root, <App />);
};Solution
Removing the | None branch will result in a compilation error:
Error (warning 8 [partial-match]): this pattern-matching is not exhaustive.
Here is an example of a case that is not matched:
NoneError (warning 8 [partial-match]): this pattern-matching is not exhaustive.
Here is an example of a case that is not matched:
NoneBasically, the compiler is telling you to handle the None case if you want to ship your app. This is part of what makes OCaml such a type-safe language.
2. What happens if you rename the _evt variable inside the button callback to evt?
<button onClick={evt => setCounter(v => v - 1)}><button onClick={evt => setCounter(v => v - 1)}>Solution
Renaming _evt to evt results in a compilation error:
Error (warning 27 [unused-var-strict]): unused variable evt.Error (warning 27 [unused-var-strict]): unused variable evt.By default, OCaml wants you to use all the variables you declare, unless they begin with _ (underscore).
3. Comment out the [@react.component] attribute in Counter.re. What happens?
//[@react.component]
let make = () => {
  let (counter, setCounter) = React.useState(() => 0);//[@react.component]
let make = () => {
  let (counter, setCounter) = React.useState(() => 0);Solution
Commenting out [@react.component] in Counter.re will trigger a compilation error in Index.re, at the place where Counter component is used:
File "Index.re", line 3, characters 19-27:
3 |   let make = () => <Counter />;
                       ^^^^^^^^^^^
Error: Unbound value Counter.makePropsFile "Index.re", line 3, characters 19-27:
3 |   let make = () => <Counter />;
                       ^^^^^^^^^^^
Error: Unbound value Counter.makePropsFor now, don’t worry about what Counter.makeProps is or where it came from—just remember that you need to put the [@react.component] attribute above your make function if you want your component to be usable in JSX. This is a very common newbie mistake. See the PPX chapter for more details.
View source code and demo for this chapter.
- Despite the name, don’t confuse OCaml’s switch expressions with JavaScript’s switch statements. ↩︎