Developing for each platform individually is so 2010s. These days, with React Native’s component system providing base-level UI constructs, we can efficiently develop for every platform while not sacrificing any particular platform’s uniqueness. Taz will review the approach they’ve taken while developing Guild, and the creation of their Mondrian cross-platform responsive design system as well as how they’ve accommodated the differences in navigational experiences across platforms to accomplish a codebase that can be 95+% shared across every platform.
React Native Everywhere
From:

React Summit 2022
Transcription
I'm Taz Sing and I'm here today to talk about React Native Everywhere. And by everywhere, yes, I mean everywhere, even down here with these lovely penguins. For those of you that don't know me, I'm the founder of Guild. It's a new platform that's in beta at the moment aimed at elevating communities around the world. Currently, we're focusing on events like the one we're at right now and presentations like the one I'm giving. All hosted on the same platform. So come chat with me later if you're curious about the platform. But by now you might have noticed that my accent is certainly not from around here. And that's because I'm from Toronto, where I started the Toronto JS community back in 2010. Back in those early days, I was subject to a lot of banter about JavaScript. Oh, you aren't a real developer, they used to say, unless you program in C++ or.NET or Java. And for me, it was like taking a back at that, because if you've ever built a JavaScript web application, you know how much diligence goes into it. Back around 2010, we started to build apps using Backbone and Underscore, which were immensely complex beasts. So I started Toronto JS to get folks together and learn from each other. To elevate the craft together. And a lot of what we did back then serves as my impetus for starting Guild today. That's because I quickly discovered that communities like Toronto JS are everywhere. We're in a similar community here right now. In fact, a lot of my closest friends are people that I met via the community, some of whom are in this very room. So I packed my bags and moved to London in 2017 to explore those communities around the world. And it was also then that I was introduced to React Native on every platform. Here you can see me giving a talk about that in London just after I moved. This tweet is from Ellie, who's actually just over there. She's an organizer of that event where I spoke and now a very good friend as well. You can find that presentation posted on Guild by heading over to my profile, clicking on the presentations I've given, clicking on 6 and 1, that will take you to a video of the talk I gave back then. So feel free to check it out. But to give you a very quick rundown of that talk, essentially the goal was to have common code run on every platform. Of course, each platform is different and requires unique consideration to properly address each platform's advantages. This is one of the core strengths of React Native, where you can push that uniqueness to the boundaries and keep the core of your codebase common for every platform. Things like date selectors, file inputs, notifications, maps, and more can be addressed along the edges, leaving the internals of your application to focus on the common experience. For us, an early stage startup, it's important for us to optimize developer efficiency. We want every change to our codebase to be as impactful as possible. We don't want separate teams working on each platform that will result in no code sharing at all. We don't want some low level of code sharing that maybe some elements are shared but the majority is still separate. In an unrealistically ideal world, we'd want 100% of our code to be shared across every platform but that wouldn't address the uniqueness of individual platforms, and in my opinion, React Native makes it easy to respect each platform's individuality, and by leveraging that, you're able to create truly unique mobile experiences, or sorry, native experiences. Realistically, what we want is the vast majority of our code shared across every platform, aiming for 95 plus percent code sharing and making our developers as impactful as possible. Of course, feel free to go check out my talk from 2017 where I speak about this more in depth and I also include all of the webpack config to make it happen. But these days, all of that is packaged up with an expo. So take a look at what they have. I recommend checking out their documentation and they have great guides on getting up and running on, like, with a platform to run on every platform. Make any app, run it everywhere. So today, I'd like to focus on the next stage of my journey, responsive composition and navigation. Let's start with responsiveness. It's no secret that multiple form factors exist. When building an app that works everywhere, we obviously need to take this into account. You'd expect a platform like React Native to account for this but it really just provides the basic primitives and critically, there's no CSS, which means there's no media queries, there's no CSS grid, there's no CSS selectors at all. Styling in React Native is inspired by CSS in that they use CSS property names and have a Flexbox implementation but CSS itself doesn't exist in React Native. If you've ever built a responsive web application before, you can start to see how challenging it is to build those same UIs without any of these web-based constructs that you're used to. Okay, so let's talk about composition. Let's assume you have React Native code that looks like this with a rendered UI on the right. We're just rendering a basic list of items here. The design doesn't really look that great. So let's add a background color and some padding to that container. Okay, now the items are a bit squished together as well. Let's add a bit of spacing between those. And so you can see that the bottom three items have a padding top applied but the top one doesn't. And that's because if we apply the same padding, it would be too much padding with the container as well. So we remove the container padding, then we mess around with composition where if we have multiple of these rendered within the same container, the padding would just be off throughout. And the code is also quite repetitive. We wouldn't really write code like this in reality. So we refactor to something like that where, as you can see, the array that we're mapping over might be data from a server or something and we're really looking at the array index to determine what styles to apply. So fundamentally I find myself doing this quite a bit and trying to pass down that style prop for every single component. I found you had to kind of police the style prop passing which is kind of annoying when you have a team of developers and you're trying to make sure they're passing it down correctly. The composition just didn't work. So what do I do? Well fortunately I went to consult in Bangalore in 2017 and the organizers of the React Bangalore community got a hold of me and asked if I wanted to present for their community. Communities are everywhere, after all. And so I agreed and here I am talking about a Pontiac Aztek. It's a truly magnificent car. I had the pleasure of getting to know the organizers of that community. One of them is Siddharth, who's sitting next to me in this photo and he's actually speaking on the other stage. I'm not sure if this is the first time the speaker's been on both stages at the same time. And thank goodness I met him because he's now one of the most foremost experts in the React design system space who I look up to for knowledge in that area. Around the same time in 2020 that I was looking for a solution for my React Native application, Sid came up with React UI. This is taken from the React UI website itself where it says React UI comes with a set of components that are accessible, responsive, and customizable. That sounds like exactly what I want. It has a list of components that it shifts with. I'm just reading from the right there. There's element, link, select, text, text area, and so on. And critically at the bottom, there's also stack and grid to help you build a layout with those other building blocks. But the problem was React UI is built for the web and what I was building was for React Native everywhere. But I wonder if maybe I could create a React Native design system, maybe those layout constructs could help me with the responsive composition of my React Native applications. And so I tried doing exactly that. After a bunch of trial and error, I got something working and I thought to message Sid. I said, hey, Sid, how's it going? Enjoying Amsterdam? I wanted to reach out to say I'm a big fan of React UI. In fact, such a big fan, I ended up implementing large parts of it for React Native in a cross-platform manner. It was a shameless rip off React UI, but of course React UI is for the web and what I've done is for React Native. Thus, there's no emotion, there's no CSS, I had to implement stack a bit differently, and so on. Sid kindly responded, that's really cool. React UI is a ripoff theme UI, ripoffs all the way down. As I've been known for saying, good artist copy, great artist steal, right? Looking at those dates on those messages, you can see this was peak lockdown in 2020 that I was working on this. And this is an example of what I built back then. You'll see this if you go to the presentation screen on Guild, and this is straight production code straight from our code base. Hopefully just by looking at the code on the left, you can see how it maps to the UI on the right. The critical construct that makes all this composable is that stack component, where I just have to set the gap property and the rest renders based off of that. No more passing down a style prop, and in order to set all the spacing, everything just works automatically. At the bottom, I render a user item component, where if I pull up the code for that and highlight that with a red dotted line, you'll see what's rendering there as well. Again, it's just a stack. This time it's horizontal, it renders a user's primary photo, and it also renders a link component with the user's name. By simply composing stacks, we're able to write our entire UI in a manner that glues together nicely. I think that's what they call being a full stack developer. We can take that and place it within a grid, which you can see here. Each stack is a row, which has a grid with columns within each row. And again, by simply composing these stacks into a grid with columns, we can build that entire page on the right. As you can see, it's fully responsive while also being highly declarative. Okay, so at this point, I was pretty happy. I got it working. All of Guild is built in this way, and it seems to work quite well. So I came up with a snazzy name. Mondrian. It's named after the famous Dutch design system engineer, Piet Mondrian, known for composing stacks into UIs that look like this. And so I created an empty repo on GitHub and I started preparing for a public release. I was really excited to share it with all of you. But that's exactly when I found out about NativeBase. So let's compare NativeBase with Mondrian, shall we? All right. It's an every-platform React Native design system. It's far more comprehensive than what we've built. They're at version 3.4.6. They have accessibility. They also have documentation. And critically, it was built in Bangalore, which was the inspiration for Mondrian all along. So these days, I would recommend a design system for you, I would say just go use NativeBase. They've done a fantastic job. I think a few of the team are here today as well if you want to talk to them. Okay, so what's next? Navigation. Well, there's mobile and desktop navigation. And on these platforms, it's more typical to navigate using the UI itself. You tap on a button, you navigate somewhere else. Essentially the UI is the core navigational construct without much else in terms of external controls in order to manipulate that UI. There's the Android back button, which is kind of the exception, but I won't get too deep into that one for this talk. Next up is web-based navigation, where the browser has a back button, a forward button, a refresh button, a URL bar, and tabs. Each of these manipulates the navigation externally, essentially to the application. I don't have to explain how a browser works, but the main navigational difference is essentially the URL bar, where you should be able to take that URL and paste it into a new tab and it should just work. To demonstrate that, here is my cat's Instagram page. She's adorable, isn't she? If we navigate using the UI, if we tap on an image, it opens up in a modal. You'll see that there's a background to that modal because it has the context of what to put in that background. However, if you copy and paste that URL into a new tab, there is no background. It opens as a full page for that image. And again, this is a core difference between mobile and desktop navigation, where mobile and desktop can work in the first case, but web has to work for the second case. So how do we go about building this? Well, there's React Router, which advertises a port for browser-based DOM routing as well as React Native. And while this does work very well, it lacks the native bindings on mobile to allow things like dragging from the edge of the screen to go back, you know, like that really slick native feel, it doesn't have that built in. Then there's React Navigation, which is a full-on mobile-first navigational experience. If you've built using React Native before, you've likely used React Navigation. It uses React Native screens from Software Mansion in order to enable that drag from the side of the screen functionality. And on mobile, it feels amazing, it feels great, it feels native, it feels proper. In recent years, they shipped web support, as I'm showing here. And so we tried this. And it just didn't quite work very well. Like while it technically works, a bunch of things just felt heavy and it just felt off. Things like incremental loading of routes, which you obviously want on a web experience, you don't want to load everything in one gigantic payload on the web, it just made the URL bar also evaluate incrementally. The URL bar would just rewrite itself, essentially as the routes loaded in. The back and forward buttons didn't quite work either. So if you click back, forward, back, forward, back, forward, the whole app would just get in a weird state and it wouldn't be able to recover, so you'd go and refresh and then your URL bar would start rewriting itself all over again. So to summarize, React Router has great state management, but it doesn't quite feel native on non-web platforms. React Navigation state management is a bit wonky, but it has that native feel. So when building a 95 plus percent shared code base with same navigation for every platform, we picked React Navigation despite its flaws. The UX won't be ideal, but users will just have to live with that and the wonkiness will just be there. I know the team is smart and they're working very hard on making it better. I tried lending a hand by sending pull requests, but it quickly became apparent that a library with the heritage of React Navigation requires more consideration than I was able to dedicate. But we'll just wait, we'll upgrade, I'm sure they're working on it, and as they release new features, essentially we'll have them as well within Guild. We hope the user experience will improve with every release. And yeah, that's my talk. Thank you, everyone. Come on, come on, I'm just kidding. That would be a terrible way to end a talk. There's no solution. What, what, what, what, come on. Jeez. I'm disappointed in all of you. If you've learned anything by now, I solve all my problems with incessant complaining to folks in the community. And this time, I was complaining to my good friend Lorenzo, pictured here at Phil and Cindy's wedding. It's the only photo I have with him. And I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding. I'm not even kidding.