DEV Community

Matt Hagner
Matt Hagner

Posted on • Edited on

Getting up and running with Reason, React, and Parcel

UPDATED: 4/23/2019

Recently the Reason React project released new bindings. It uses a new version of "react-jsx", and utilizes the React Hooks API instead of the old record based API.

This means that if you have experience with hooks, you'll feel pretty much right at home with Reason React, with some small differences.


With the rise of TypeScript on the front end, I think there will be an increased willingness to learn and utilize typed, compile-to-javascript languages. One strong contender for me is Reason.

You may have heard about Reason and thought about learning it, but it's hard to give up the JavaScript tools you already know and love. The good news is that Bucklescript and tools like Parcel make Reason a breeze to work with.

The following will show you how to set up a Reason React project with Parcel, but teaching you Reason, or how Reason React works compared to React in JavaScript are outside of the scope of this article. If you'd like to learn more about those things, let me know in the comments and I'll gladly write more.


Quick Background

What is Reason?

Reason is a special syntax that will look extremely familiar to JavaScript programmers, that uses OCaml under the hood.

Why does Reason use OCaml under the hood?

Facebook has been using OCaml for a long time to write some of their internal tools (Flow is written in OCaml). OCaml is a fast, mature language that has great type inference, strong type safety, and can compile to many different targets including JavaScript, and Native.

What is Bucklescript?

Bucklescript is a compiler that takes OCaml (or Reason), and turns it into highly optimized, and surprisingly readable JavaScript.

What will we be using today?

The set up we are using will use Reason as the language of our code, reason-react as the front-end framework, Bucklescript for compiling our Reason code to JavaScript, and Parcel for bundling everything up.

Expectations

To get the most out this, you'll want to be comfortable with React.


Initialization

The first we are going to do is make a new folder for our project, and change into the directory.

mkdir reason-parcel
cd reason-parcel

Then we are going to initialize using either npm, or yarn, and install all of our dependencies.

To use the new Reason React bindings you'll want to make sure that you are using bs-platform ^5.0.0, and reason-react ^7.0.0.

yarn init -y 
yarn add -D parcel-bundler bs-platform bsb-js
yarn add react react-dom reason-react

Scripts

Now open up your package.json file, and we are going to add a start script. Add the following to the bottom of your package.json

{
    "scripts": {
        "start": "parcel src/index.html"
    }
}

Set up index.html

Let's make a directory called src. We need to make an index.html file inside of src to match the start script we added to the package.json. While we're at it, we might as well make the file where our Reason code will live.

mkdir src
touch src/index.html
touch src/Main.re

You can use whatever HTML boilerplate you'd like. I highly recommend emmet, and if you use VSCode it's already available. Or you can copy the following:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title></title>
</head>
<body>

</body>
</html>

Inside the <body> tag of index.html add:

  <div id="app"></div>
  <script src="./Main.re"></script>

There are two important things to note:

  1. The id of the div is "app". This will be used later inside of our Main.re file to render our app.
  2. The src property on our script element coincides to the name of the file we just created. Parcel is smart enough to see this src, and know that it needs to process the Reason file, and replace the src property with the outputted JavaScript file path.

Set up bsconfig.json

In the root of your project make a file called bsconfig.json.

touch bsconfig.json

Bucklescript will read from this file for configuration. If you want to read more about all of the available options for the config file, Bucklescript has great documentation.

Copy the following config into bsconfig.json

{
    "name": "reason-parcel",
    "reason": {
        "react-jsx": 3
    },
    "version": "0.1.0",
    "sources": [{
        "dir": "src",
        "subdirs": true
    }],
    "package-specs": {
        "module": "commonjs",
        "in-source": true
    },
    "suffix": ".bs.js",
    "bs-dependencies": ["reason-react"],
    "refmt": 3
}

Let's go through some of the interesting properties:

  • reason
    • reason-react: Is set to 3 to allow the new JSX features.
  • sources
    • dir: Is set to the src folder we made earlier.
    • subdirs: Is true because this tells Bucklescript to recursively check within our src folder for Reason files.
  • package-specs
    • module: commonjs is the default 👍 which is good enough for us.
    • in-source: Will make our outputted JavaScript files appear next to their Reason counterparts.
  • bs-dependencies: This is an array of the Reason specific libraries you depend on. Right now we are only using reason-react, but you could use component libraries, styling libraries, etc.
  • refmt: should be set to 3 for the ReasonV3 syntax.

Let's Finally Write Some Reason

Now with all of that out of the way, let's write some Reason code, and view it in the browser!

Open up src/Main.re and copy the following exactly. Semi-colons, double-quotes, and all. We'll run through exactly what is happening shortly.


module HelloWorld = {
   [@react.component]
    let make = () => <h1>{React.string("Hello, World!")}</h1>
};

ReactDOMRe.renderToElementWithId(<HelloWorld />, "app");

Now save the file, and run yarn start or npm run start, and open up localhost:1234/

Alright so there's a few things that are probably familiar, and a few things that are probably weird.

The Familiar

Rendering to element with a specific id:

ReactDOMRe.renderToElementWithId(<HelloWorld />, "app");

That should look pretty familiar to you if you've written React before. It's essentially doing the same as:

ReactDOM.render(<HelloWorld/>, document.getElementById("app"));

JSX

For the most part, JSX in Reason is the exact same. There are some small caveats to HTML attributes that name-clash with Reason reserved words like the type property on input elements becomes type_.

The Weird

What is with the module declaration?


module HelloWorld = {
};

The full explanation goes a little deeper than the scope of this article, but in Reason a file is automatically a module.

To create components in Reason React you need to either declare each new component in a new module, or in a new file. By wrapping our component in module HelloWorld ={}; it allows us to use the JSX element <HelloWorld />.

What is this [@react.component]?

This is the recommended way to make a new Reason React component. It does some stuff for us behind the scenes which makes our lives easier, but if you want to learn how to manually create your own components without it, check out the new page on components in the Reason React documentation.

What is the make function?

let make = () => <h1>{React.string("Hello from Reason")}</h1>

Similar to how in React class components React looks for a render method, in a Reason React module, Reason React expects a make function. This make function will take all of the props as an argument, and simply return some JSX.

Is that really what I have to do to make strings?

React.string("Hello, from Reason")

Yes. Yes it is. At least that it is how you have to make strings that are valid Reason React elements. Stay tuned for the next section for some tips to make strings in Reason React less painful.


Let's Refactor a Little

First let's pull out our component and put it in its own file.

touch src/HelloWorld.re

Then we can copy and paste the HelloWorld module, but this time because it's in its own file so we don't need to wrap it in a module.

Inside of HelloWorld.re

let s = React.string;

[@react.component]
let make = () => <h1>{s("Hello, from Reason")}</h1>;

I also made it easier to call React.string by assigning it to a short variable, s. This technique is something you'll see quite frequently in Reason React code.

Now let's go back to our Main.re file and delete everything besides the last line.

Main.re

ReactDOMRe.renderToElementWithId(<HelloWorld />, "app");

Notice how we didn't have to explicitly import HelloWorld into Main.re? This will seem extremely weird at first, but because of how OCaml's module system works, all file-level modules are globally available under the file name.

So in Main.re we have access to the HelloWorld module. By using it in our JSX Reason React knows to call the module's make function.


Now you have all of the pieces necessary for starting a Reason React project with Parcel. Feel free to play around with things, and let me know what questions you have regarding Reason, Reason React, or Parcel.

P.S. I wouldn't feel right without quickly mentioning bsb as an alternative for bootstrapping Reason React projects:

bsb -theme react -init <PROJECT_NAME>

To use the bsb command you will have to install it globally

yarn global add bsb

Or use npx (if you're using npm 5.2 or newer you already have it installed) to run the command

npx bsb -theme react -init <PROJECT_NAME>

Top comments (0)