Why is it recommended to use *.bs.js suffix?

Is there any advantage of using *.bs.js suffix other than to make it easier to gitignore the rescript output files?

Any inconveniences of using just .js?

"suffix": ".js",


One advantage is, that when you import the generated .bs.js file from JavaScript, you can immediately tell, that it is a generated file coming from Bucklescript / ReScript.

import { make } from “./foo.bs”


import { make } from “./foo”

This and some other benefits are also mentioned on the website:


thanks, this is helpful

My guess is in the future the default will be changed to .res.js. This will make imports even more transparent, e.g. you would do

import { make } from "./foo.res"

And it would be resolved to ./foo.res.js but in the JS file it looks like you’re importing the .res file directly thanks to the suffix.


It’s also good to know by filename which files are generated (this allows you for instance to suppress the diff of generated files on GitHub code reviews).

This is unrelated, files will automatically be marked as generated as soon as github spots a GENERATED pattern in the first line.

Bob actually expressed the idea to set the default to .js at some point, not sure if this will be done though. The bs.js suffix default will definitely go away, and it’s most likely not going to be .res.js, since it has all kinds of resolving issues in different setups (webpack resolve, etc), which would force the users to write myfile.res.js.

With .js, there wouldn’t be any issues memorizing the extension patterns. Create a file button.re and on the JS side, import as `./button’;

Ah that’s pretty cool (is that new? because I had to set this line src/**/*.bs.js linguist-generated in the .gitattributes file some time ago to make it work), didn’t know that. Not sure how pervasive that is implemented among tools though. Having a pattern in the filename is nice though, it makes certain actions like deleting all generated files much easier.

It would be nice to be able to write ./MyComponent though, so that is definitely a benefit!

1 Like

Hi, it’s due to some historic reasons.
Originally we support .js as default (it still is), but later we need some capability to remove staled output. For example, when you rename a.res into b.res, we need have a way to delete a.js and other staled artifacts. It’s difficult to do such things without a clear marker, .bs.js. There’re some other benefits.
In the latest release 8.3, we have some improvement in the build system to remove staled output, so the benefit is minor, we can remove staled output whichever file extension you pick.
We also support customized file extensions including: .js, .cjs, .mjs, .bs.js.


It’s not on the to-do list. The benefit is unclear while .res is a suffix of .res.js which could cause some trouble in module resolution

Using .js can simplify a lot of things when dealing with js tools that don’t expect dots in file names (or where a dot is used to declare an inner module), like Serverless for example. I guess it’s totally alright when you don’t mix rescript and js in your code.

I think it’s also totally fine when mixed, unless I am missing something obvious?

no no it’s alright, I was thinking of some peculiar folder structure when the JS file has to conform to a certain template, I’ve seen people following this architecture:


where MyFile.js is reexporting MyFile.bs.js in a certain way for some JS tools. In such cases you can’t generate .js files without using different base names for the rescript and js files. But with rescript becoming better and better at JS interop (see latest improvement with ES6 imports), this kind of structure is less and less needed anyway.

When you mix JS and rescript, the suffix also makes it easier to know which files were generated in a glance.


good tip! I didn’t know about that one

additionally I added this to .gitattributes although I’m not sure if it is still necessary/correct

# Tell github that .re and .rei files are Reason
*.re linguist-language=Reason
*.rei linguist-language=Reason
*.res linguist-language=Reason
*.resi linguist-language=Reason

Btw, the ReScript PR to linguist is still open.

bummer, looks like the branch is out of date

My best understanding of how this works in Linguist is that, when version headers are not disabled for bsc, the version header output matches the Linguist check for coffeescript generated files which start with “generated by” and have a suffix of .js (https://github.com/github/linguist/blob/a74ab061516869ff266de303580523e72899527f/lib/linguist/generated.rb#L190)