Video details

Building Cross-Platform Apps with React Native + Next.js | Fernando Rojo | App.js Conf 2022

React Native

One of the main benefits of using React Native for us has been the ability to share non-UI code between our web applications and our mobile applications. In this talk, Fernando focused on how to properly set up a monorepo with shared code and React/React Native apps and how to build and deployment configurations for Expo, Vercel, etc.. He also covered architecture choices, authentication flows (same backend API but very different frontend), common problems, and some other issues we've encountered.
Make sure to follow us on Twitter not to miss any news on the next edition of App.js Conf!


Thank you. Before we start, let's take a quick group selfie so I can get all you guys in this. All right, look your best. Put your arms up or something. So let's talk about shipping to production with React Native and XJS. So my name is Fernando Rojo and like I mentioned, I'm the Cofounder and CTO at Bgig. So we're a marketplace for booking artists. You can think of it like Airbnb for live music. Our product is live in production and processing millions of dollars worth of bookings. We have a website and we have an app, but we don't have a web team and a native team and a design team. In fact, we don't have a front end team at all. The front end for the website and the Native app was built and designed by one person with one code base. And that's all thanks to using React Native with Next JS. I first spoke about React Native with Nextgs at next. Comp in 2021. Some of you might have seen that, and in that talk I discussed the many challenges I faced, including navigation, animations, responsive design systems, platform specific features, and mono repos. And I walk through how I built a few libraries to solve the problems, such as dripsy Modi Salito. Looks like one of them might be missing. Maybe you'll find out at the end of the talk. And the reception of the talk surprised me a bit. I thought that React Native with Next year would be pretty niche, but it turns out that there is a real appetite among React developers to be less constrained when shipping product. And little by little, a sizable community has started growing around the stack. Could it be that the promise of cross platform without compromising user experience or avoiding the best frameworks like Next JS was finally coming true? People started trying it out and there was a lot of excitement. But there's a pretty big elephant in the room. How do I handle navigation? My DMs were flooded with this question all over GitHub. Everyone's asking how do I do this? And navigation is by far the hardest part of cross platform development. So in my next talk, I discussed a library I made called Exponext React Navigation. And as the name implies, this library lets you share code between Expo and NextJS with React Navigation on the native side. So why were people still getting stuck? The reality is that the APIs offered by this library were pretty bad and didn't solve the issue. So this is what it looked like to use Exponents React Navigation. And you can already see like, where is this taking you? On Native we have a route name, and on web we have a path. And these might not match. And it's just pretty unclear where you're going to end up from this. The difference in abstractions between screen names and URLs is already enough to confuse you. Next JS uses a pages folder with file system routing, whereas React Navigation uses component based stacks, tabs, drawers, and other forms of navigation that all go through a single app TSX file. And overall, web and native just have very different patterns. And a key component of the library that was missing was just documenting how you should deal with these. So if you go down the rabbit hole, you'll find that I worked with Axel and a lot of other people on a pull request in an example app, trying to solve this problem for months, basically from November when I gave a talk, until the day I released Solito. And here at the top you can see a comment from someone who saw the talk, was really excited, they had a web development background, and the first thing they're wondering is like, how do I get navigation working? And this slowly became one of those poll requests that had a ton of activity. After all, the most basic thing of any product, just like how you get from screen to screen, remains very unclear. And it's useful to take a step back here for a second. Previous attempts at sharing code between web and native have made you share too much, resulting in apps that look like websites or websites that look like apps. And so this is why cross platform over time has gotten a bad reputation. And that's the trap I wanted to avoid. I wanted each platform to look and feel natural while still sharing the underlying screens that should look the same no matter where they are. And this required abstracting at the right level. And eventually we figured it out. And the result is a library called Solido, which I guess has been mentioned a few times. Now, I was hoping it would be a bit more climactic here, but Solido lets you share navigation code across React, native and XJS with the exact same API. It uses Next router on web and React Navigation on native, and it never crosses over between the two. So you're never importing Next code on native or vice versa. And this is all possible thanks to a recent feature in the last few years from React Navigation with a great linking config. So all of your linking on the native side is handled with a single variable. Solito has the same API as Next JS, so if you've ever used Link or use router from Next JS, this will look very familiar. Since most people here obviously use React native, you're probably used to using something like navigation navigate with a screen name. Solitaire replaces that altogether and forces you to always use URLs. And using URLs instead of screen names is much more scalable. It creates a strict contract between developers and users that over time you're forced to stick to. It also makes features like deep linking just work out of the box when you want to map your web URLs to your native URLs. And finally, the problem of cross platform navigation was solved. So let's get into what Solido does and purposely doesn't do. Solido intentionally has a very limited scope. Give it a URL, it detects what platform you're on and it will make sure you get to the right screen. And a key part of Solido is documenting the recommended patterns and app structure. When building with expo and XTS, this is one of the key things that's come up over time. The code isn't really the hard part, it's just knowing the right structure and what to do. And so since we've been doing this for a few years at BK, that's what I've been trying to share in the docs. And over time, I'm going to be adding more elaborate examples and guides to the Solido website. The API provides three things. First, a set of link components. There's a text link, emoji link, and just a basic link, a use param hook which replaces use route from React navigation. What's really cool about this is it looks just like Used State, but it lets you use your query parameters on web and just your screen parameters on native to update state. And finally you get to Use Router hook, which lets you push and replace screens with URLs. Now, what's important to note about Solido is that it doesn't concern itself with how you render your screens. It views your screens as your app's primitives, but it's up to your app and website to render these however you want. You can use tabs, stacks, drawers, whatever you want. You're not constrained at all. It doesn't involve itself with how it actually looks. As long as each screen points to your URL, it works with Solido. So oftentimes when someone releases a new library, like, I've been getting asked, like, will this work with Salito? And it's not really even a question you have to ask. If it works with React native, it works with Solido. This makes it easy to keep your website looking like a website and your app feeling like an app. They share the same screens, but the outer layout and navigation implementation is up to the platform itself. Setting up a mono repo with React native and XJS is a pretty big pain. So Solido comes with a script to solve this for you. You just run this and you have a fully configured mono repo with an expo entry point, an X entry point, and all the shared navigation. And over time we're going to be adding a lot of more examples. One that I'm super excited about is this tailwind CSS, react native. One, Mark Lawler has been working on a really exciting library that lets you use pure CSS on web for tailwind and then Build Time takes strings into style objects on the native side. So I think it's going to be really cool for anyone who's used to using Tailwind. So just to kind of recap the lesson I learned, building solito here we have the BEAC mobile website on the left and the iPhone app on the right. Notice that the screen looks the same, but if you look around the edges, you'll notice some differences. The navigation sort of layout is very platform specific and that's by design. And this is what you do. It lets your users get the experience they expect on each platform. And that's the beauty of abstracting with URLs and not using prebuilt opinionated UI on web. While that might be good for native on a website, you really want to customize everything from the header to the menus that pop out. And I learned a weird lesson here. It took me years to realize that this is the right approach, but it took me like 4 hours to write the initial code. If you open the Solido library and you just look through it, it's super basic, it's just a few components, a few hooks and that's it. And for over a year I was just abstracting at the wrong level. And the best answer turned out to be the simplest one by far. So when you're solving cross platform problems, it's important to focus on finding the right abstraction and embrace the things that each platform already offers, rather than trying to overfit one to another. I'm going to totally switch gears here and talk about menus. So here we have Windows 95 and since the dawn of time, every UI has had menus. This is literally like almost 30 years ago. And yet I've always found menus to be a big pain point in React native. If you wanted to create a menu in react native, how would you do? You would probably set up some bullying to track states to see if it's open. And you would then have to measure the location of a press able and put it under it, or you just absolute position it under it whenever it's open. But let's say you want to do some more complicated things like you want to be able to tap outside of the menu to close it. So that doesn't work necessarily on native. You can't set like a global listener when you click outside of something like you could on web. So you'd probably have to set up a modal now, right? You put a modal and it kind of has a transparent background and you can just tap the back of that and that will close the menu. The problem is, for starters, iOS only allows one mode at a time with react native. So if you ever already have one open elsewhere, that could cause some problems. And you're also having to track state. So you also now have to measure the position of the trigger of the button that triggered it. And then you have to measure the size of the menu and let's say it doesn't fit on the screen, you have to move it accordingly, many issues like that. And so even if you solved all of that, you're still left with issues like animations, Android, back button support, just the many accessibility problems. And if clicking a menu item does something like sort of list, you're probably going to get a frame drop when it closes. And so by this point, it's probably pretty clear why there isn't a leading menu library in react native. Even though we had this way back in the day. I've been putting a lot of thought into this and wondering, given that menus are already so hard in react native, would it be possible to make a menu for web, iOS and Android that works in react native? It's already a bit too hard to do on one platform, but maybe be easier if we try to do it for all then. So for the last few months, I've been working on experiment what if this menu problem could be solved once and for all? And I focused on two key questions here. First, what is the ideal API for a menu? And second, what should users experience for the ideal API? I set on Radix UI. I think it's just like super elegant, very easy to use, very composable. So I made it my goal to use Radix as the API and figure out the rest from there. Since this only works on web, I needed to find a suitable implementation for iOS and Android. And this is where historically, when people build cross platform frameworks and apps, they get a bit tripped up. The goal is not to share everything. If all you want to do is share code, you can just put your website in a web view and you're good to go. But your users don't care what your stack is, they just want a good product. So while Radix is the best option for menus on web, both from an API and implementation standpoint, I decided to use true native menu components on iOS and Android. It's important to build with a single unified API, but the output to users doesn't have to be the same everywhere they are. And so today, I'm introducing an experimental library called Zigo for building menus with react native. And React native web. Zigo lets you use the same API as Radix UI under the hood. It will create the best menu experience for end users. Depending on the platform that they're using, the menus themselves will not look the same across platforms, and that is by design. We want to leverage the best that each platform has to offer. So on iOS, it'll be a native iOS menu. I'm also going to be adding a context menu so you can hold down on something, and it kind of shows the elements imessage style with options below it. You'll be able to do checkboxes dangerous actions, things like that. And on Android, we're still working on the native side. So currently it's just a JavaScript based implementation for the meantime. But it will also be using the native menus on Android. So while this is still pretty early, don't even have a full doc site set up like I like to in general and stuff like that. You can start using it now and keep in mind that it's first release and I'm going to be adding more components over time. Hopefully a pop over and tool tip and things like that. Our natives are just going to render the same thing whereas on web you'll be able to interact with it with hovers tapes but this has already been in production on the Beacon website and app for some time so in my experience it's a bit battle tested and it's working great and it will work with Expo as long as you use a custom def client. But as we learned yesterday, there's no more rejecting out. You will not have to use this with Expo and that is all I have. So if you want to stay updated with what I'm working on you can follow me on Twitter. It's probably the easiest way and if you want to learn more about the open source stuff that we're building a beak and be part of the growth that we have as a company we are looking to hire some fantastic people so feel free to shoot me a DM or find me later today during the conference. And I think that is all. Thanks so much.