Best practices for using ReasonML in a monorepo


Hi all! I’m working on a ReasonML app where we’re using react-native + react-native-web to build some universal components that we intend to use in both web and native (iOS/Android) apps. We’ve set up a monorepo (using lerna) with the intention of having a package for our UI components that can then be utilized in both the web and native apps.

I’ve got the web app set up to use the UI components package as bs-dependency. But, I’m starting to wonder if I’m working against the build system by doing it this way. For instance, I get a bunch of “Duplicated Package” warnings because of two copies of ReasonReact and bs-platform, and I think I may actually have an issue with an other lib I’m using that’s being caused by it being used in two different instances.

That got me wondering if anyone’s got any tips for using Reason in a setup like this. It seems to me that I could just include the directory containing my UI components in the web app’s sources array, as well as the native app’s, and they’d both treat the components as source code instead of a dependency… though I’d lose the namespacing and the theoretical ability to run a different version of a component in different apps, I suppose.

I’d love to hear if anyone else has set something up like this and has any tips or ideas to share. Thanks!


I would suggest you use yarn workspace feature to hoist all packages to top level node modules. I’m doing this in my project and it works great for me


I would really appreciate any examples on how to set up a monorepo so that the editor support and dev workflow (building and package dependencies) work. :+1:

I’ve been struggling to make this work. :frowning:


This has been working well for us so far:

  • Setup Lerna with Yarn workspaces
  • bsconfig.json at top level
    • List all your sources in your packages
    • List all bs-dependencies used in all your packages (including your monorepo packages)
    • Make sure to remove the namespace option
  • Normal bsconfig.json in each of your packages

For example we put all our reason code in a src/ folder in each package so our top level bsconfig.json looks something like this

  "name": "myorg",
  "version": "0.0.0",
  "sources": [
      "dir": "packages/thing1/src",
      "subdirs": true
      "dir": "packages/thing2/src",
      "subdirs": true
  "package-specs": {
    "module": "commonjs",
    "in-source": true
  "suffix": ".bs.js",
  "bs-dependencies": [
  "warnings": {
    "error": "+101"
  "refmt": 3

With this if you use MyOrgThing1.Whatever from thing2 the JS output should properly show it requiring from “@myorg/thing1/src/whatever”.

Note that when you make a change in one package and you want it to reflect in another you’ll need to restart the watch process.


Awesome, that is super helpful @carbcola :+1:

I have something similar set up now. Also worth noting for anyone else who may care - I installed reason-cli at the monorepo root.

Working all rad except for this last point:

Note that when you make a change in one package and you want it to reflect in another you’ll need to restart the watch process.

That really grinds me :frowning:


Also we realized today with the setup I outlined above you’ll need to keep your filenames across all packages unique! I kind of assumed this wasn’t the case with namespacing on in each package but it makes sense and the namespaces are still super useful.


One thing that I noticed is that .merlin was confused by scoped dependencies in the bsconfig.json files for each of the dependencies. Removing the scoped prefix fixed this for me.


Why do you use yarn workspaces instead of plain npm ? Lerna can hoist dependencies without problem, so I don’t see the advantage here.


Just to add to the list of possibilities, I’m using rush with yarn and so far so good