Docs / Language Manual / BuildConfiguration


bsconfig.json is the single, mandatory build meta file needed for bsb.

The complete configuration schema is here. We'll non-exhaustively highlight the important parts in prose below.

Tips & Tricks

If you're using an editor like VSCode, put the following code as your first field in bsconfig.json to get schema autocompletion!

"$schema": ""

name, namespace

name is the name of the library, used as its "namespace". You can activate namespacing through "namespace": true in your bsconfig.json. Namespacing is almost mandatory; we haven't turned it on by default yet to preserve backward-compatibility.

Explanation: by default, your files, once used as a third-party dependency, are available globally to the consumer. E.g. if you have an and the consumer also has a file of the same name, they will clash. Turning on namespace avoids this by wrapping all your own project's files into an extra module layer; instead of a global Util module, the consumer will see you as MyProject.Util. The namespacing affects your consumers, not yourself.

Aka, in Bsb, "namespace" is just a fancy term for an auto-generated module that wraps all your project's files (efficiently and correctly, of course!) for third-party consumption.

We don't do folder-level namespacing for your own project; all your own file names must be unique. This is a constraint that enables several features such as fast search and easier project reorganization.

Note: the bsconfig.json name should be the same as the package.json name, to avoid confusing corner-cases. However, this means that you can't use a camelCased names such as MyProject, since package.json and npm forbid you to do so (some file systems are case-insensitive). To have the namespace/module as MyProject, write "name": "my-project". Bsb will turn that into the camelCased name correctly.

Note on custom namespacing: if for some reason, you need a namespace that is different from what your name will produce, you can directly send a string to the namespace option. For example, if your package is a binding named bs-some-thing, you can use "namespace": "some-thing" to get SomeThing namespace instead of BsSomeThing.


Your source files need to be specified explicitly (we don't want to accidentally drill down into some unrelated directories). Examples:

{ "sources": ["src", "examples"] }
{ "sources": { "dir": "src", "subdirs": ["page"] } }
{ "sources": [ "examples", { "dir": "src", "subdirs": true // recursively builds every subdirectory } ] }

You can mark your directories as dev-only (for e.g. tests). These won't be built and exposed to third-parties, or even to other "dev" directories in the same project:

{ "sources" : { "dir" : "test", "type" : "dev" } }

bs-dependencies, bs-dev-dependencies

List of ReScript dependencies. Just like package.json's dependencies, they'll be searched in node_modules.

Note that only sources marked with "type":"dev" will be able to resolve modules from bs-dev-dependencies.

reason, refmt (old)

reason config is enabled by default. To turn on JSX for ReasonReact, specify:

{ "reason": {"react-jsx": 3}, "refmt": 3 }

The refmt config should be explicitly specified as 3.


Hook that's invoked every time a file is recompiled. Good for JS build system interop, but please use it sparingly. Calling your custom command for every recompiled file slows down your build and worsens the building experience for even third-party users of your lib.


{ "js-post-build": { "cmd": "/path/to/node ../../postProcessTheFile.js" } }

Note that the path resolution for the command (node in this case) is done so:

  • /myCommand is resolved into /myCommand

  • package/myCommand is resolved into node_modules/package/myCommand

  • ./myCommand is resolved into myProjectRoot/myCommand

  • myCommand is just called as myCommand, aka a globally available executable. But note that Bsb doesn't read into your shell's environment, so if you put e.g. node, it won't find it unless you specify an absolute path. Alternatively, add #!/usr/local/bin/node to the top of your script to directly call it without prepending node.

The command itself is called from inside lib/bs.


Output to either CommonJS (the default) or ES6 modules. Example:

{ "package-specs": { "module": "commonjs", "in-source": true } }
  • "module": "es6-global" resolves node_modules using relative paths. Good for development-time usage of ES6 in conjunction with browsers like Safari and Firefox that support ES6 modules today. No more dev-time bundling!

  • "in-source": true generates output alongside source files. If you omit it, it'll generate the artifacts into lib/js. The output directory is not configurable otherwise.

This configuration only applies to you, when you develop the project. When the project is used as a third-party library, the consumer's own bsconfig.json package-specs overrides the configuration here, logically.


Either ".js" or ".bs.js". Strongly prefer the latter.

Design Decisions

Generating JS files with the .bs.js suffix means that, on the JS side, you can do const myReScriptFile = require('./'). The benefits:

  • It's immediately clear that we're dealing with a generated JS file here.

  • It avoids clashes with a potential theFile.js file in the same folder.

  • It avoids the need of using a build system loader for ReScript files. This + in-source build means integrating a ReScript project into your pure JS codebase basically doesn't touch anything in your build pipeline at all.

  • The .bs.js suffix lets bsb track JS artifacts much better.


Selectively turn on/off certain warnings and/or turn them into hard errors. Example:

{ "warnings": { "number": "-44-102", "error": "+5" } }

Turn off warning 44 and 102 (polymorphic comparison). Turn warning 5 (partial application whose result has function type and is ignored) into a hard error.

The warning number are shown in the build output when they're triggered. The complete list is here, a little bit below. 100 and up are ReScript-specific.


Extra flags to pass to the underlying bsc call. Notably: ["-bs-super-errors"] for turning on cleaner error output. No need to pass this flag for a new ReScript project; it's enabled by default.

Environment Variables

We heavily disrecommend the usage of environment variables, but for certain cases, they're justified.

Error Output Coloring: NINJA_ANSI_FORCED

This is mostly for other programmatic usage of bsb where outputting colors is not desired.

When NINJA_ANSI_FORCED is set to 1: bsb produces color. When NINJA_ANSI_FORCED is set to 0: bsb doesn't produce color. When NINJA_ANSI_FORCED is not set: bsb might or might not produce color, depending on a smart detection of where it's outputted.

Note that bsc, the barebone compiler, will always be passed -color always. See more details in this issue.

BS_VSCODE error output adapted for VSCODE

If you run bsb under VSCODE task runner like this, it is recommended to have BS_VSCODE set