Open-source project
reasonml/reason-react avatar
reasonml/reason-react

reason-react: an opam package with an npm manifest that is not the package

Reason bindings for ReactJS

3,262 stars344 forksReasonMIT

At a glance

What is it?
React bindings for Reason, written by the team behind the language and now pointing ReScript users at a different package. The repository is an OCaml project: dune builds it, a preprocessor generates the JavaScript, and the npm manifest exists only for development dependencies.
Who is it for?
Use reason-react if you write Reason and want React components with a type system that catches the mistakes the JavaScript version lets through. It sits on top of React rather than replacing it, so it fits an existing project without a rewrite.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 5 days ago.
What is it written in?
Mainly Reason, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • reasonml/reason-react3,262 ★ · +0
Star history
Repository2026-09-242026-11-14
reasonml/reason-react3.3k3.3k

0 Stars in the last 51 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

The first line tells ReScript users to go elsewhere

The README opens with a warning, and it is aimed at the largest group of people who might arrive here.

For ReScript users the recommendation is a different package. The explanation is a history lesson: ReasonReact was designed to be used with BuckleScript, and after the language was renamed and reorganised it got packaged into the successor library, with a link to the write-up about that move.

So the audience for this repository is narrower than it looks. It is for people writing Reason, not people writing ReScript, and the difference is a rename plus a reorganisation rather than a technical change.

The rest of the pitch is the opposite of narrow, though. ReasonReact is described as a safer, simpler way to build React components, and the compatibility claim is blunt: it is just React.js under the hood, which makes it easy to integrate with an existing Next.js, Create React App, JavaScript, Flow or TypeScript project. The integration story is not a rewrite; it is adding a binding next to what you have.

Bindings are the unit, and this package is one of them

The section on JavaScript libraries explains the concept the whole project rests on, and it does it with a comparison you already have.

Just as TypeScript has type annotations, this project has bindings. A binding is a library that lets you import a popular project, a utility library as an example, or a local file of your own. And then the line that reframes the repository: ReasonReact is itself an example of a binding.

That is worth sitting with. The library is not a compiler for React or a wrapper around it; it is one binding, written in the same way anybody else would write a binding for their own code. Which means the pattern generalises: the same approach works for the twenty other libraries your project uses, and the compiler expansion is what makes the types line up between the two languages.

It also explains the two conference talks the README links, both about the future of the language and React together, and the reference to a full example application maintained in a community organisation rather than here.

A component is an attribute on a let binding

The example is three lines and contains the whole mental model.

reason
/* Greeting.re */

[@react.component]
let make = (~name) => <h1> {React.string("Hello " ++ name)} </h1>

A component is a let binding carrying an attribute. The binding is named `make`, which is what an OCaml module generator would use for a single value, and the body is a JSX element.

Two details are specific to a statically typed language meeting JSX. The element takes a labelled argument rather than a props object, so the parameter is written as an optional label and consumed with a prefix inside the body. And a string cannot be a bare child, because a Reason string is not a React node, so it has to be wrapped: the concatenation is a language-level string build, and the call converts it into something React can render.

That wrapping is the small tax you pay for having the type system, and it is the kind of detail that only becomes obvious once you write your first component.

The distribution is an opam package, not an npm one

The tree is an OCaml project and the packaging follows from that.

There is a dune file and a dune project file at the root, an opam package file for the library, and a template file beside it, which is how the opam metadata is generated rather than hand-maintained. There is a second opam file for the preprocessor, so the expansion ships as its own package.

Then there is a preprocessor directory. That is the part that makes the whole thing work: a preprocessor runs over your source before compilation and generates the JavaScript, and in this project that generation is what turns a component attribute into the JavaScript React expects. So a build of your Reason project involves this library's preprocessor as well as its runtime library.

The remaining root files describe the toolchain rather than the library: a formatting configuration for the OCaml formatter, a lock file and configuration for the Nix flake, a gitignore and an npmignore, and a configuration file for a translation service.

Now the npm manifest, which is the piece most likely to confuse somebody browsing the repository. Its version is a placeholder zero, its author field is empty, and its own description says the file exists to install node development dependencies. It is not the published package. The package you would depend on is the opam one.

Two test runners, one of which runs against compiled output

The test setup has two halves and the make file makes the difference explicit.

One half is the OCaml side. A target builds and runs the project's own test alias under dune, and a second target runs them in watch mode. A third target, named for promoting snapshots, rebuilds the tests with a flag that updates the expected results in place rather than failing on them. Snapshot testing with a promote step is a normal choice for a project whose outputs are large and mostly structural.

The other half is Jest, run through the node package manager. And its configuration is the interesting part: the roots point into the dune build output directory, three levels deep, and the test pattern matches compiled test files whose names end in a specific compiled-language suffix. So the Jest suite does not test Reason sources. It tests the JavaScript that the compiler emitted.

There is also a debug target that prints instructions to open a browser debugger and then launches the test runner with a breakpoint flag attached. That is a small thing that tells you the intended debugging loop: compile, then attach.

Formatting is dune-native too, with a check mode and a write mode, both routed through the same alias so the formatter the project uses is the one in the formatter configuration file.

The demo is three files, and the docs live in the tree

The example directory is the clearest possible statement of what this project produces.

It contains a dune file, an HTML page and one Reason source file. That is the whole deployment: a page, a build rule, and a component. There is no bundler configuration, no transpiler step written by hand and no wrapper library, because the preprocessor and dune already produce the JavaScript the page loads.

Beside it there is a documentation directory and a website directory, so the documentation is maintained in the repository rather than described in the README. The README is therefore an index: it links the documentation site, points at a getting started page for adding the language to an existing project, links a page of small examples, and points to a separate editor plugins page for people who want syntax highlighting in their editor of choice.

There is a translation configuration file as well, and a changes file. So this repository follows the conventions of a project that expects to be maintained for years rather than a demonstration: changes are recorded, the docs are versioned with the code, and the interface is translated by a service rather than by hand.

A slow release cadence, and an OCaml switch pinned in the build

Two maintenance signals, one from the releases and one from the build file.

The release history is unhurried. Three releases, roughly a year apart: one in August 2024, one in July 2025 and one in December 2025, each at a minor version, none carrying the letter prefix that many projects use on their tags. The last commit in this snapshot is dated 2026-09-28, so work continues between releases rather than only at them.

That is the profile of a library tied to a language and a compiler rather than to an application. There is no user base to chase, and changes that depend on a compiler release will wait for that release.

The build file pins the rest. The local switch creation target asks for a specific compiler version, and the dependency install target installs the opam dependencies together with the test and development setup sets, then forces a node install. So reproducing a build means matching a compiler version, which is the one thing that will bite a contributor on a machine that is set up for something else.

Editorial conclusion

Use reason-react if you write Reason and want React components with a type system that catches the mistakes the JavaScript version lets through. It sits on top of React rather than replacing it, so it fits an existing project without a rewrite. Do not start a new ReScript project on it, because the README itself points those users at the package that absorbed it after the language rebrand, and carrying two ways to bind React is worse than picking one. Before you commit, note the build assumptions: an OCaml toolchain with a switch, dune, and a preprocessor step, plus node dependencies for tests. And read the licensing and packaging situation carefully, because the npm manifest in this repository is not what you publish.

Frequently asked questions

What is ReasonReact?

React bindings for Reason, described as a safer and simpler way to build React components. It is React.js under the hood, so it can be added to an existing Next.js, Create React App, JavaScript, Flow or TypeScript project rather than replacing it.

Should ReScript users use ReasonReact?

No. The README recommends a different package for ReScript users, because ReasonReact was designed for BuckleScript and was packaged into that successor library after the language was rebranded and reorganised.

How do I write a component in ReasonReact?

As a let binding carrying the component attribute, with a JSX element as the body and a labelled argument instead of a props object. A Reason string cannot be a bare JSX child, so it is wrapped in a call that converts it into a React node.

How is ReasonReact distributed?

As an opam package built with dune, with a separate package for the preprocessor expansion that generates the JavaScript. The npm manifest in the repository is not the published package: its version is a placeholder, its author field is empty, and its description says it exists to install node development dependencies.

How do I run the ReasonReact tests?

Two ways. The dune target runs the project's own tests, and a companion target rebuilds them with a flag that promotes updated snapshots instead of failing. Jest is a separate path that runs against compiled test files inside the dune build output, with a debug target that attaches a browser inspector.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. reasonml/reason-react on GitHub
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/reasonml-reason-react.svg)](https://hysenlabs.com/projects/reasonml-reason-react)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/reasonml-reason-react.svg)](https://hysenlabs.com/projects/reasonml-reason-react)

Was this useful?