What are the benefits of module interfaces for React components

In my work, I find it very useful to have interfaces for modules that have domain entities and business logic (think anything that has type t in there). With React components, the benefits are much less obvious for me, because:

  1. The noise they add is significant
  2. The presentation components already tend to fail fast

So: are there serious benefits of having interfaces for modules containing React components.

@yawaramin, you offered to take it to the forum, here it is :slight_smile:

There are a few concrete benefits off the top of my head:

  • Compiler performance: If you edit an interface, then the compiler has to recompile every module that depends on the interface, but if you edit the implementation, then it only has to recompile the implementation. If you don’t have an interface file, then the implementation and the interface are the same, so editing the implementation will always trigger a lot of recompiling.
  • Runtime performance: An interface file helps the compiler inline code that isn’t exported, which generates smaller bundles and usually increases runtime perf as well. (Not to mention more warnings about unused code.)
  • Controlling a public API and tracking breaking changes: If you change an implementation but not its interface, then often that means that haven’t broken your module’s API. If you have to change the interface, then you know for certain that there’s a breaking change.

And this is more subjective, but I think that interface files are always helpful for organizing and reasoning about your code. For example you may open a .res file with hundreds of lines of code and ask yourself “what’s all of this doing?” But if you glance at its accompanying .resi file and see that it’s just exporting one React component, then that makes its purpose a lot clearer. Similarly, a component may come with a bunch of helper functions or types. What is their purpose? Do other modules need to use them too? An interface file helps answer these questions without requiring you to search through the entire project.

6 Likes

Also:

  • Interface file is also the perfect place to put documentation comments. You can thoroughly document components in the interfaces without burdening the implementations with high volumes of comments making it difficult to navigate through the implementation details.
  • Interface files are the specific technique used by ReScript Next.js bindings to enforce only components are exported, to allow hot reload to work correctly.
5 Likes

Very valid points, thank you both. I’m still not sure they apply to all the possible cases (e.g., some components may be quite small, not require any comments, and not have a lot of dependents), but otherwise benefits seem rather big.

IIUC, the bindings in Patrick’s template cater to the Fast Refresh’s specifics.

already gave up on that strategy a long time ago… i mean i still use interface files for compilation speed, but i use plain JS files in pages/ to re-export ReScript components… less to think about and easier to predict the behavior… also easier to prevent naming collisions when doing dynamic pages, or when changing things around.

Interesting, I wasn’t aware. Wasn’t that one of the main reasons that symbol support was added for filenames in ReScript? I.e. page-based routing like /foo/[id].res.

This was partly a side-effect; the original intent was to allow filenames like MyTest.spec.res or Button.ios.res.

From a practical perspective, it was pretty hard to explain all the different scenarios how to expose components correctly (res / resi combinations, with or without @react.component), and how to e.g. allow duplicated [id].res in different subfolders, which is pretty common. It is easier to tell users to just import their ReScript components in their plain JS files, which lets them have more fine-grained control over the actual exports of the page and don’t make them feel annoyed whenever they need to play around the compiler to get the output they want (e.g. get rid of $$default).

For simple cases it might suffice, for complex scenarios I am not too sure.

3 Likes