Kadi
Thank you. Thank you for that intro Mo. Um, I was going to explain the 1800 packages, but I think it's more fun when I don't. So let's chat about that later. Bonjour, je m'appelle Cady. I mean that is, thank you. That is the extent of my French, I'm afraid. So we're going to continue the rest of the talk in English. But yeah, I've been building React Native apps for quite a long time. And aside from that, I have been having quite a bit of fun teaching React Native when I get the chance. So here's a picture of me teaching React Native as proof, I guess. And these days, I'm a developer at Expo. I'm working on kind of a mix of things. So some CLI stuff, some hosting stuff, making educational content, helping our amazing community and Discord. By the way, special thanks to all of you who answer questions on our Discord. You know who you are. React Native is a web-inspired framework. So let's talk about how we can further lean into our roots in the web and then create the best cross-platform experiences. But first, some facts. As of this year, 60% of global web traffic comes from mobile devices. And furthermore, mobile users spend 90% of their time on their phones using apps rather than mobile websites. I'm sure you've felt this yourself that mobile web can be rather torturous at times, whereas in-app experience tends to be much faster and smoother. There's also business sense in it. So for example, in e-comm, it is proven that conversion rate, meaning people buying stuff, is much higher on mobile devices. So there is business sense in creating a good mobile experience and driving users towards it. Mobile has clearly overtaken desktop when it comes to market share. And while some communities do gravitate to using mobile exclusively, it would be pretty naive to think that desktop will ever go away. This is the historic data from the past 16 years, and you can see that it's kind of normalized to a 60/40 split with mobile being higher. So, let's look at the data. So when it comes to the question of whether we should build a mobile app or a website, it becomes kind of unnecessary because of course we want to have our cake and eat it too. And importantly, ensure that the two experiences are well integrated. This talk is about how to get the best out of both worlds with Expo. Everyone in this room at some point has rolled the tech dice and luckily for all of us, we have landed in specializing in highly transferable technologies. So we are able to specialize in both web and native. In this talk, I'm going to share four ideas for how to get the best out of web integrations with Expo. For as long as I've been building React Native apps for clients, I've been asked about code sharing. I mean, to be fair, it's usually, can I put my existing React web stuff in React Native? The answer to which is yes, but that's point four. This point is about writing a component once and then using it on multiple platforms, so web and native. I am of course talking about React Native Web and React Native Web is still an odd concept, especially those of us who arrived at React Native from the web side, because we think of React Native as like a special version of React that makes it possible to target iOS and Android. But actually, if you look at what React Native is, it is an API and a spec. So if you write a view, it maps to an Android native view and an iOS native view. And I mean, that's all we get out of the box from meta, but we also have out of tree platforms. So we have a tvOS, which has their own implementation of a view and macOS and Windows, etc. But this also lets us map this view back to a div on the web, giving us this one API to target all of these platforms. This does not mean that we are now limited to just out-of-the-box React Native elements. We can, of course, use web-only code on the web platform, including just return regular old HTML. And this makes this approach really extensible for when new approaches to using React Native on the web surface, assuming that they support gradual adoption. I find that an easy shared styling system is also really important. So there's quite a few cool styling systems out there. I think more recently I've enjoyed using native wind, especially when code sharing between web and native. So native wind is obviously tailwind on native and it makes it really easy to create breakpoints for the various screen sizes, but it also has directive for like web and native, so you could add styles that only apply on a native platforms, which is pretty cool. This is an example project that I created for a demo, and it is an e-commerce app that just has a list of products, a product listing page, and a PDP, which is a product lead-us page. So this here is a product listing page. It is one code base and it works on iOS, Android natively, and the web. Not only that, but it's also responsive. Just to give you an idea of how I built what I just showed you. So I've used a shared product card, so the code is identical between web and native. But I've created a if statement for the layout file. So for the web, I find that it is easiest, I find that it was easiest to create this responsive layout using a scroll view with the styles that adjust the columns. And then on native, it's obviously a flat list. And this is an example of a product details page. So on native, I want the product details page to be a modal and go on top of the other content. But on desktop, I definitely don't want the modal in the middle of my product details page, I want it to be a separate page with a top header nav. So this I actually did in a layout file. So I made the screen options platform specific. So presentation modal or native and add a header on the web. Now, this might like seeing some of this code might give you this fear of spaghetti code, and that is a very valid fear. But I do feel that this can be avoided or managed with a mindful approach. So just some ideas in general to use platform extensions. So the same way that you would with React Native components themselves, you know, you have a view so that it's just one API and then under the hood, it maps to the various platforms. So you could do it yourself. So rather than having just bits and pieces of platform specific code, you encapsulate it into single files. I also find that platform select is really handy for selecting individual pieces of config. And another thing is using the same if statements. So in my examples, I always use if platform is web, then return early, and then a native is the default case. So it's better to keep it one way around rather than mix if platform is web and then if platform is native and then if platform is not web, et cetera, which makes code harder to read. I mean, these are just my thoughts on it, but really I recommend asking how to avoid spaghetti code from the experts at Blizzco. React Native Web might not be the right approach for all apps, but using React Native in the web has literally never been easier, especially with router. And I do believe that web targets for React Native are going to become more common, so it's worth giving it another go. What I've shown you is a very small example of a universal app, and we are working on creating more examples, but if you want to have a look, here's the link to the demo I showed you. Next up, we have server-side code. I mean, I love a good to-do app, a good local to-do app with nice animations. But sadly, chances are that your app calls out to some external APIs. And a lot of the times it's an API that you need to build and maintain. So this means that you need to create a new project and deploy it and maintain it and keep it in sync. And it's a whole audio. If only we could just write this API code in our React Native code base. Well, with API routes, we can. Basically, if we add plus API to the end of a file name, it becomes an API route and we can export functions as for the HTTP methods. In this example, I have a get and a post method. But it's just a regular server-side function, same as like a Lambda function or Google Cloud function. So you could put pretty much anything here. For example, a GraphQL endpoint or an OAuth redirect. I'll group this in an API folder because I want the URL to be /api/greeting, but you could also, for a bit of chaos energy, just sprinkle them anywhere in your codebase. Then when your MPX experts start, these get served on port 8081 and you can call them in your app, browser, etc. But how do we deploy them? Well, API routes are parts of the web project. So to deploy them, we need to export the web project into a deployable bundle. So we run npx export export platform web, and then this creates a dist bundle in server mode, which will have a client and a server folder. So I open up the server for all this so you can see, so it creates a separate JavaScript file for each of my API routes. As an aside, we're in an Expo router code base here. You might not want complete parity between your mobile and web experiences. In this case, you can adjust the web UI accordingly. For example, in this case, I've just used the root layout file to block most of the routes from the web. Anyway, once you have your dist folder, you can use esdeploy to deploy the ES hosting. So it will take a couple of seconds and you will get a URL with the deployed API which you can now use in your app. We also have a snazzy dashboard where you can see all the requests for your new API and website, including a surprising amount of metadata. So you see the request path, the country, the device that they used, how long it took, in milliseconds, geographically where they're from, etc. And another cool thing that's actually built in is error stack traces. So if there are any errors in your API routes, you'll be able to see them on a dashboard and makes debugging easier. So to learn more about API routes and hosting in general, I think the hosting announcement blog post is still the best way to get started, so it gives an overview and then gives you links to resources to get cracking. All right, next up, I want to talk about universal linking. Basically, when you look at any popular product these days, they have a website and a mobile app. And importantly, they are able to link between the two. And they often link to a specific screen in their app. So for example, you will link to a specific product, or you will link to a specific post. It's also useful for authentication flows, because it protects against the man in the middle attack. Thank you. This is what universal linking is. It's basically a way to securely deep link into your app, but using a web URL of a website that you own. Technically on Android, they're called Android app links, but actually if you Google Android universal links, you get to the app links page. So I think at this point, Google have agreed that Apple chose the better name and just going with it. Okay. All right, this is basically what's happening. So above there, I've got link in example.exporterapps. So this is a domain that I own, a website that I own, and I've set up universal linking on it. So given that universal linking is set up and I am on my phone and then I click a link on, let's say, an email, that's a link to a website. Linking example.exportapps/products/1. Now, if I have the app installed, if I don't have the app installed, then it will just open on a browser. If I do have the app installed, then it depends on whether this particular URL is configured for deep linking. So a lot of times you don't want your entire website to try to deep link into a browser. an app, but only a subset of routes. So if this URL is not configured for universal linking, we open in a browser, otherwise we try to open product/one in the app. And another cool thing about Expo Router is the whole point of file-based file-based routing is that you would be able to do universal linking. There is a one-to-one mapping between your website and your mobile app. So by default with Xperia Router, it will open products/product ID, which is pretty neat, but in some cases you might want to redirect your routes, which you can do with the native intent. The way this works essentially is that we set up a two-way association between your website and your mobile app. It's not difficult per se, it's just a little bit tedious. Basically, you have a special file on your website that proves you know the mobile app and you have a special file, you have a link to your website on your mobile app. So both prove that they know each other. It's kind of like referencing system. To link the website with your app, we add a special file to a special endpoint. So it's well-known Apple App Site Association. And this is a public endpoint. You can, for example, go on facebook.com/well-known-apple-app-site-association and you can see this. The important part here is the app ID. So this consists of your developer ID that you paid $99 to have. And yeah, that's what it's used for. And the app ID that you chose. And this combination will make your app globally unique. On the Android side, it is similar, only that you want to prove that you're associated with the app by listing the SHA-256 certificate of the key store that your app was signed with. Now, to prove that the app is associated with the website, we just need to embed the website URL in your app bundle. With Expo and CNG, it's just a couple lines of code in your app config. Without CNG, it's still a couple lines of code, but directly in your native folders. And finally, we want to configure which URLs we want to link to. So we can pick and choose. On the iOS side, this is listed in the site association on the web, and then on Android, this is actually part of the app config. So the app configures which links get the native intent. That's pretty much it. You just need to rebuild your app and reinstall it. And the universal links get configured and reset when the app installed. So basically what happens is when the app is installed and if the universal linking URL exists, it hits the endpoint, sees if there's a site association file, and if so, the links get configured. This means that when you change these things, you need to reinstall the app. Anyway, if you want to set it up for your app, I've written a blog and a YouTube video about it recently, so check it out. And the final topic I want to cover is DOM components. DOM components are a new-ish, you've probably heard about them before, they're a new-ish concept in Xfra Router, but basically they allow you to pull bits and pieces of web code into your native app. So for all the clients way back then who asked me about putting React web code into React Native, this one's for you. I'll just show you how this works in code. This is a DOM component. It lives in your React Native code base, but you can see that it is a regular React component returning some HTML. What makes it a DOM component is that useDom directive at the top. And basically what this does is the bundler will see the useDom directive and stick that code in a web view. This means that you'll be able to use web only APIs in there. So then on the native side, you can just use it like any other component, including parsing and props. Now, you might be thinking, well, I could do that myself already with a WebView, which is true, but what makes DOM components special, apart from how easy this is, is that you can also interact with the native app from a DOM component. So here's an example of an onClick event that's being triggered on the web, in the web view in the DOM component, and it's being handled on a native site. I mean, in this case, we are literally just console login, which is not very exciting, but this could, for example, trigger some haptic feedback or call into other native APIs. So this means you can have little, um, little kind of web components that are highly integrated into the rest of your app. So they don't feel like web views bundled everywhere. So what should you use a DOM component for? I don't think you should take your whole website, break it into pieces and stick it in your app. Not only are most of the time React Native components are more performant if you write real ones, it also wouldn't be very performant to have hundreds of web views in your app. So really, we want to be smart about it and use it for things that are really hard to do in native, but already have good solutions on the web. For example, I really love this demo from Cedric. I think it's a perfect use case for DOM components. So he used the expo default template and basically embedded a TL draw in a DOM component so you get a collaborative infinite drawing canvas that took him five minutes to make, well work on as a proof of concept and he works on iOS, Android and web. So other great examples are things like rich text editors or really fancy charts and things that your web team has taken months and months to build. If you want to try out DOM components, our docs are a great resource for getting started. And finally, I just wanted to quickly share some things that I'm really excited about for the coming year. Releasing hosting has been incredibly exciting for us because it is the first time there's been a really easy integrated experience to have server code in React Native. So it's an easy way to do auth, easy way to have secrets, easy way to have an integrated website. And it's mostly a springboard for making more future web integrations way easier. But secondly, there are so many exciting things coming in Expo SDK 53. So this is in a couple of weeks. And I mean, Brent's changelog post will really highlight all of them, but I've picked out some of the things that I thought were most interesting. Obviously we're going to have new architecture by default and with that React 19 finally. But we're also going to have Jetpack's Compose support for X4 modules. So X4 modules already had Swift UI support for the iOS side and then Jetpack's Compose is the Android equivalent. So it's good to have parity there. The SDK team has been incredibly busy as always. They are amazing. I don't know how they do what they do. So, but I had to pick one, which I think is really exciting. So we're going to have a new Expo Maps. So it will be a brand new library and it is going to be opinionated. So it is going to be built on Google Maps on Android and then Apple Maps on iOS. And finally, I'm not sure how much we actually talked about it, but SDK 53 will also have the first version of export UI. And export UI is basically going to be DOM components, but in reverse. So we're going to have native UI components that we can use in our React Native app. So imagine if you have your Apple settings page, these elements you could put together and use in your app. Here are the links from earlier on the talk. Thank you so much for listening.
Mo
Thank you very much, Caddy. You're not getting out that easy. Damn it. Someone's going to ask me about links. I can feel it. So tell me about links. So you can ask any Q&A questions that you have. I will be filtering them, so be nice. Let me start off with something. So firstly, it's quite interesting stuff that you're working on because I think this whole vision of universal apps is quite exciting and the ease of integration between native and web on the DOM components is really quite cool. But also we need to compete with AI, so we need all the edge we can get. We'll be like, we can build for all the platforms. Exactly. We are the AI masters in the React Native world. So I guess the one question, I was quite interested by the server code and running server-side code. What's the runtime that that's running on? Can you run node libraries within it or is it running on the edge runtime?
Kadi
Ooh, that is a great question. So on EOS hosting, we are built on top of Cloudflare workers for platforms. So it is the Cloudflare worker runtime, which is very Node, like similar, but it's more like Node on the web. So we have a lot of like compatibility layers and gyms and stuff that we've built to like add more of what we expect from Node in there, so there's not currently like a hundred percent parity, but we're working on making it like as similar as possible.
Mo
Yeah, yeah. No, that's totally fair. And that was what I expected. So I was just curious.
Kadi
Yeah, so there is, we do have a docs page. So on the expert docs, if you go on a hosting section, at the bottom we have like, like there's a whole page about like what APIs are supported and what aren't supported and what aren't supported yet.
Mo
Cool, very cool. Well, Cloudflare workers are a pleasure to work with. So I'm sure that this is gonna be equally as fun to build with. Who's used Cloudflare workers here before? You're all missing out, honestly. You should give it a try. Alright, let's go to the audience Q&A and see what we've got. Did you build part of your example app with AI?
Kadi
No.
Mo
Cool. What's the best approach to--
Kadi
Oh, actually, sorry. I did the data with the AI. I said, like, give me funny tech products. Yeah.
Mo
That's perfect use case for Aya. Nice. What is the best approach to migrate from React Navigation to Expo Router, and have you dealt with that yourself personally?
Kadi
Oh, interesting. I think first, thing that you need is the desire to do it. No, I'm just saying like, you know, for example, you don't need to use Expo Router to use Expo, right? It's like, React Navigation is also something that we built. And, you know, Expo Router is built on top of navigation, but like not on necessary. So you don't have to migrate like for the sake of it, is what I'm saying. So it's not like, oh no, I'm like, like, like leaving, you know, I want to use Exfo, but I'm not using Exfo router. I have to, that's not, that's not the case. But how to migrate? I think it depends on the app. So what I would do is I would draw out the navigation tree. So in native, we are dealing with a navigation tree, rather than a navigation stack, right? So you take the screens that you have and then the navigators you have, and then draw it out and then the difference between React Navigation and Explorer Router especially is that in Explorer Router you have layout files, right? And you can't do things like, oh, if user is logged in, return these screens, otherwise return these screens. Instead, these things are handled by our redirects. So highlight all of these code paths that diverge within a statement, and then figure out how you're going to do those things. with the redirect and then just like do a simple app with like just simple screens that handles that navigation flow and then you can migrate across.
Mo
Yeah, makes sense. But I think that is the most different thing is the redirect side of things and people have that pattern of not mounting screens if things aren't authenticated.
Kadi
Yeah, I have a video coming up really soon, which explains how layout files work. And I was really excited to finish it. So I'm like, I'm going to share it with the world, but it's not out yet. So when it comes out, watch it.
Mo
Well, stay tuned for that. How much can we rely on self-hosted EAS websites for an API? Can it handle a lot of simultaneous requests? I'm not sure with the self-hosted.
Kadi
I mean, for example, you can deploy the server side with like Express But it's like, you know, it depends where you deploy it. If you have an express server on a Lambda, then it will handle as much as you throw at it.
Mo
And I'm presuming the runtimes will be different as well there.
Kadi
Well, yeah. So when you build locally, you build on node anyway. So it's just like you need a node compatible runtime. But there's no dependency on CloudFace, for example. So basically, even though ES hosting is the easiest way to deploy your XO project, it is not the only way to deploy XO project. So you can always use your own Express server.
Mo
Cool. And we'll do one more question, just in the interest of time. What's your favorite hidden endpoint that you found by going to an app's.wellknown page?
Kadi
I'm not sure I have a favorite hidden endpoint, but I have explored them and I've been amazed at how many apps big companies have that we've never heard of. Even if you go to the Facebook one, they have separate apps for marketing and sales and apps that I've never heard of, so that was quite cool.
Mo
Very cool. Well, Cady, thank you very much. Thanks for the amazing talk and all of the answers. Give her a big round of applause.