Mazzarolo MatteoMazzarolo Matteo

Mistakes I made while maintaining an open-source React Native library for five years

By Mazzarolo Matteo


In this post, I'll share some details around design decisions and mistakes I made while working on React Native Modal, a Modal component library for React Native.
I hope that sharing my thoughts may help other new open-source maintainers to avoid such errors.

This post focuses on a React Native library, but it's not strictly related to React Native. It's more about generic design and maintainability decisions.
This should be a beginner-friendly post. Seasoned open source maintainers are probably already familiar with the topics explored here.

2016 — Open Sourcing a React Native library

I started using React Native in late 2015 at my daily job. Back then React Native was still in its infancy.

In early 2016 I open-sourced a tiny React Native library to enhance the capabilities of React Native's built-in Modal component. The built-in Modal is a thin API layer to present content above an enclosing view. It is a low-level API, in the sense that it just offers a way to "show" something, but it's still up to the developer to handle the styling, animation, and behavior of whatever they present.

The goals of my library were to:

  1. Show a backdrop beneath the modal.
  2. Animate the modal entrance/exit.
  3. Allow dismissing the modal on backdrop touch.

The initial surface area of the project was small, and it didn't allow many customization options.
The entire project was 86 lines of code.

2021 - React Native Modal, today

It's August 2021 now, and the modal component is known as react-native-modal.

Thanks to all its contributors, react-native-modal now offers a ton of customization options and features: it's swipeable, scrollable, user-friendly, and it patches a few quirks of the native modal implementation. And, as expected, the codebase has grown exponentially.

react-native-modal has more than 4.5k stars on GitHub, and has/had many contributors. I feel like most of its success is due to:

The sore point

It's not all fun and games, though.
Since 2020, react-native-modal development has slowed down.

Around two years ago, I moved to a different role at my company, and I'm not using React Native anymore. Additionally, I have several other side-projects I'm trying to maintain. So, I'm not able to actively develop new features in react-native-modal, nor provide the same grade of support I used to.

I'm still keeping an eye on important issues and ensuring it stays compatible across React Native updates. Also, some folks are helping from time to time (thank you all, especially @ancyrweb), and I'm still open to onboarding new people on the project.
But we're far from being as active as we were a couple of years ago.

Overall, I think react-native-modal is still a nice library. But I also feel it could be even better if I made different decisions in the past.

Mistakes have been made

Besides a few small side projects, react-native-modal is my first "serious" open-source library.

While developing and maintaining it, I had a lot of fun, learned new things, and made some poor decisions.
In hindsight, to me, most of these poor decisions sound "obviously" wrong now. But, hey, that's how experience works and how you learn.

And that's also why I think that sharing these decisions and the thought process behind them might help other new open source maintainers.

Mistake #1: Not having a clear goal and direction

I created the first version of react-native-modal because I needed its features in my daily job. Then, I decided to open source it.
I didn't open source it to solve a specific problem. I just wanted to give back to the community and (maybe?) receive contributions (e.g., bug reports, new features).
And it worked!
There was an issue, though: react-native-modal didn't have a "real" goal.
Without a clear direction, react-native-modal quickly became a huge catch-all modal solution. We often added features on top of features just because the native modal API exposed by React Native wasn't satisfying 100% of the use cases. All of it while trying to catch up with React Native, Android, and iOS updates.
We were (and are) doing the exact opposite of KISS (Keeping It Simple, Stupid). And the codebase suffers from this. The more features we want to support, the more conditions and edge cases we need to cover.

Mistake #2: Not saying "no" enough

When someone spends hours to package an excellent pull request that introduces a new feature, it's hard to say "no" to it.
But, sometimes, I shouldn't have been scared to say it.
Quoting Jeff Geerling:

Feel free to say 'no' when a PR doesn't meet your standards. So many projects get derailed by accepting too many new features without evaluating them for long-term maintainability, and it's a problem that's avoided by a simple two-letter word.

Not having a clear goal makes it much harder to say "no" to contributions.

Especially for pull requests with new features, I should have been more forward-looking and rejected them when they weren't 100% aligned with the (unfortunately, blurry) project architecture.

Mistake #3: Using a misleading library name

For the first few months, this library was named react-native-animated-modal.
Then, after asking for permission, I moved it to react-native-modal.

In retrospect, this was a mistake.
A generic name like react-native-modal sets some wrong expectations because it sounds like the "official" modal component of React Native.
Especially for newcomers, it can be unintuitive that this is just a wrapper on top of React Native's built-in modal component. From time to time, I still see issues opened in the react-native-modal repo mentioning problems pertinent to just the React Native built-in modal.

In my ideal world, the react-native-modal library name should be used only by the built-in React Native modal (if extracted from the core repository) or by a thin abstraction layer on top of it.

If anyone on the React Native team is reading this post: If you need the npm namespace, feel free to DM me 👍

Mistake #4: Hanging back on hard decisions

Another thing I regret doing is hesitating too much on a hard decision: Moving react-native-modal to a full JavaScript implementation.
We started thinking about using "just JavaScript" because the most common issues reported in react-native-modal are tied to the behavior of the native modal implementation that we can't change.
To clarify: I'm not talking about issues of the React Native built-in modal. I'm talking about the native Android and iOS modal. Things like stacking multiple modals on top of each other weren't natively supported by the Android and iOS OS until a couple of years ago.
Moving react-native-modal to a complete JavaScript implementation would unlock a ton of customization options.
But not being backed by a native API introduces new complexities to the tables: What API should we expose to allow the modal to sit at the root element of the app performantly? How do we achieve native-looking modals (especially with the upcoming — now available — iOS modality flow)? What about accessibility?

In PROPOSAL: JS version of react-native-modal #145, I think I did a good job explaining the issue and suggesting a solution. We got a ton of helpful feedback and ideas. But I procrastinated — and never actually started working on it. I regret not making a sound decision and not leading the initiative with a complete proof of concept.

Mistake #5: Depending on third-party libraries for core features

The last mistake I wanted to mention is relying too much on third-party libraries.
Specifically, in my case, react-native-animatable.
React Native Animatable is a great library that allows defining transitions and animations in a declarative fashion by abstracting the React Native animated API. In 2015/2016, react-native-animatable was the way to sprinkle animations on top of React Native apps. Mad props to @oblador for building it.
react-native-animatable powers all react-native-modal's animation. Users can pick any animation exposed by react-native-animatable and apply it to the enter/exit state of the modal with a single line of code.

Time has passed, though. React Native Animatable is still a good solution by today's standards, but it's not as performant nor configurable as other modern options. Nowadays, there are several new ways to animate views more efficiently in React Native. Between the good-old React Native animated API, Reanimated/Reanimated 2, Moti (which can almost be a drop-in replacement for React Native Animatable), and Lottie, adding silk-smooth native animations to a React Native app has never been easier.

I don't regret picking React Native Animatable, but I do regret using a high-level API to customize the modal animation.
Most entrace/exit animations for modals are just a combination of translation and opacity interpolations. They could have been easily covered by a lower-level API (like React Native animated), and we would have avoided playing this catch-up game with the newer APIs.

Summary

I hope this post doesn't sound like a post-mortem.
I want to clarify that you can/should still use React Native Modal, if it fits your use-case.
It works well, even on the latest version of React Native.
There are some known bugs here and there, but they're all fixable. And I'm still open to onboarding new people on the project (just DM me, and/or start contributing to the repo).

My goal with this post is just to share some of my experiences, hoping they can be helpful to new open-source maintainers.

Thanks to the React Native maintainers, and to everyone who contributed to React Native Modal so far!