I've been hacking non-stop for over two weeks. During this time my tech stack has grown to many libraries and multiple services. I've done a lot of experimentation; there are parts of the toolchain that are excellent, and there are parts that are difficult to use, buggy or immature. Here is a review of various libraries and my recommendations. If you're starting with mobile development this might save you a considerable amount of time.
React Native
For a long time I didn't love React. It was clunky and complicated. Hacking around component lifecycles, higher order components, state management, and javascript’s class implementation left me frustrated with overly convoluted code. Then they introduced hooks, and transformed the library.
Hooks are tricky because they break normal expectations. There is no other mainstream library in javascript where you can't use a particular function inside a conditional. Hooks betray a high degree of complexity under the hood, and expose the bones of the library in a way that normally results in a bad user experience. To do hooks right, you'd need to design a new programming language with facilities that support their behavior natively. In a perfect world, use[X]
would be a keyword.
But the world isn't perfect and hooks in javascript get close enough. React ships with a good linter and runtime error detection, so it will warn you if you're doing something wrong. It's almost as if you were using a specialized programming language. Almost. I don't understand how people who don't have PL/compiler experience manage to understand hooks and use them correctly other than through endless trial and error. But if you do get past the language breaking semantics, the library is a joy to use. I don't think I've ever seen simpler, more straightforward UI code in my life.
All this extends to React Native. Setup is hard for the simple reason that there is a lot of freakin' code. You must understand the relationship between React, Expo, CocoaPods modules, Gradle, various forms of iOS/android configuration for essential libraries, native vs imitation components, and a hundred other things I'm forgetting. But once you spend a couple of days with it, things become clear. React Native really does deliver on write once run everywhere.
One caveat is that to build modern apps you quickly need to get beyond core React Native and start using community components. "Community" ranges from built and supported by a multibillion dollar corporation, and written by some guy in his basement and abandoned two years ago. There is a large variation in quality, functionality, and documentation. It's common to spend hours trying to figure out how to do something and having to study source code on GitHub before you can get things done.
With all that said, I haven't had to write native code yet (beyond copy pasting a few lines for library installation), and haven't had to create custom bridges to React. Once things are set up, everything runs smoothly and React Native is a joy to use. The ecosystem could get a lot better, but the state is good enough to make you extremely productive.
I haven't written pure native apps yet, so I can't do a full honest comparison. Given the information I have now, my intuition says to use React Native if you're two guys in a garage hacking a startup, then re-evaluate when you get bigger and can afford to hire multiple mobile engineers.
Tailwind
The library tailwind-rn is Tailwind CSS in React Native. The verdict here is unequivocal— it makes you an order of magnitude faster at styling, and makes your code an order of magnitude simpler. If these numbers are exaggerated, it's not by much. Use it and don't look back.
Initially tailwind looks crazy. Not only are you using inline styles, you're using weird two letter class acronyms everywhere that initially are unparseable to a naked eye. But the library delivers on all its claims. The search function on their docs is so good that after a short initial learning curve it becomes trivial to find the right acronyms and use them in your styles. You have to do it this way for a couple of days, at which point you can correctly guess 95% of the time and refer to the docs less and less.
You get a number of important benefits. First— a lot less code. Tailwind is super expressive, you find yourself expressing in one line what would normally take up half a page. That means everything is easier to write, easier to read, and easier to change. Second, you don't have to think of stylesheet names which you immediate realize was a massive waste of time because outerUserProfileWrapper
, innerUserProfileWrapper
, userFullNameWrapper
, etc. increase cognitive burden without adding any value to your life. Third, Tailwind restricts stylesheet expressiveness out of the box, until you eventually customize it. For you that's a good thing, because they're professional designers who managed to pick good defaults and do whatever magic professional designers do that make colors, alignment, typography and contrast look good out of the box.
Repeating the verdict, Tailwind will change your life. Use it and don't look back.
Pullstate
The pullstate library is an extremely underrated alternative to Redux. It uses immer under the hood, which hooks into the bowels of javascript to get you immutable, efficient copy-on-write data structures. What it means is that with pullstate you get all the benefits of core Redux without any of the boilerplate. You don't have to think of actions, reducers, dispatchers and all the other boilerplate. You can modify the data structure the way you normally would and move on with your life. Immer automatically handles all the immutability and state versioning, and pullstate gives you handy React hooks to access the right state at the right time and re-render your components as needed.
The downsides to using pullstate is that there is no ecosystem around it, and once you need to debug complex applications with sophisticated data flows, there is no toolchain comparable to Redux. If you're building something new, this is a theoretical problem because most products don't ship, and of those that ship most don't get to big complicated data flows. In this sense pullstate is perfect for rapid iteration. Whether it scales up to huge frontend apps, I don't know. It's been working great for me; if there are scalability issues later, I'll deal with them then.
React navigation
If you're old enough to have watched The Sopranos, there is an episode where Adriana meets an aspiring rock star and introduces him to Hesh, her hookup in the music business. Hesh takes one look and says: "this won't work." Adriana asks why, and what could be done to improve the music. To which Hesh replies: "I've been in this business for thirty years. All I know is good and not good, and this isn't good."
Well, React Navigation isn't good. It isn't like the other libraries where they're a pain to set up, but once you do they recede in the background and let you get on with your work. React Navigation continues being a pain. Every time I find myself stuck fiddling with something for hours to get it to work, chances are it's React Navigation. It's big, monolithic, and full of tricky, complex interactions. With good libraries, like pullstate or tailwind, the default state is that you do the reasonable thing and end up with working code. With React Navigation the default state is that you're not sure what the reasonable thing is, and once you do your best you end up with code that does something other than what you expected.
Unfortunately it's currently the only viable alternative. There isn't anything else, so you're stuck here.
Firebase
Using Firebase feels like being transported to a parallel universe where the cloud providers have a strong financial incentive to care about user experience. In our universe they don't, because the money comes from enterprise customers who put developer experience at about number 12 on the list of priorities. But in the parallel Firebase universe, the UX is great, the APIs are great, the documentation is great, and the services are great.
I haven't seen anything that comes close to firebase for getting started with a new project. No backend code, no endpoints, no deployments, no nothing. Just build your app. And when you're done building, Firebase comes with over a dozen services, from cloud functions, to A/B testing and in-app messaging.
If there is one problem with Firebase, it's that nobody at Google is using Firebase because they’re treating it as a demand generation pipeline for Google Cloud. If they were using it, they'd know that it's impossible to write good security rules by hand, or that if you do manage to write them they're impossible to debug, or that if you do manage to debug them, you'll end up with a convoluted data model to get around various limitations (such as inability to apply rules on field level).
That said, with ingenuity and some workarounds you can push Firebase pretty far. The library that helps the most is an obscure little project called fireward. Fireward is a declarative language that compiles down to Firestore security rules, and it makes the impossible task of writing secure rules pleasant. At the time of this writing you can't even find it on Google by searching for its name— you have to append "GitHub" to get to it. And it has only 124 stars. I have no idea how people use Firebase without it (but I have a guess— most Firebase apps are woefully insecure, and the only reason they get away with it is that nobody half way competent has tried to hack them).
On the client I use rnfirebase, which is a client library that efficiently accesses firebase on react native. The library is good-- it supports all Firebase APIs I've needed. You install it, it recedes into the background, and you don't ever think about it again. Which is how libraries should be.
App Center
I stopped using Microsoft software some time in 2005, and never thought I'd come back to it again. But here we are. I wanted to push my React Native code to a release branch and have the new versions of the client show up on people's phones. You can do this with any CI/CD service, but not without a lot of configuration and endless trial and error. I was excited to get the first version out, so I wanted something that just works.
I signed up for various services that do React Native deployment, and App Center turned out to be the simplest. You point it at your branch, connect it to the app stores (which is a nightmare, but it's not Microsoft's fault and no service can make that easier), and it does exactly what it promises to do. Push to the declared branch, a while later the client gets updated on people's phones. Not much more to say here, but it's a good thing— another service that recedes in the background and you never have to think about it again. For a CI/CD tool, that might as well be magic.
TL;DR:
Five stars: Tailwind-rn, Fireward
Four stars: React Native, Pullstate, Firebase/rnfirebase, App Center
Three stars: React Navigation
Check out Supabase as an alternative to Firebase, it's powered by PostgreSQL and fully open-source.
https://supabase.io/
The Firebase storage authorization mess is why I personally chose to not use it. Little to no real documentation on it, except "write some rules, then fuzz it!". No, that's not how I roll. Not with data authorization.
But I agree, great ideas and decent execution in general.
But it may not be that no one uses it. I think this comes from a certain JS sub-culture that just doesn't value good comprehensive documentation. It comes across as try it and throw something up, rather then the "Go" programming lang mindset of "study well young grasshopper" then go and program.