React Native Full Course 2026 | Build, Publish and Monetize a Full Stack Mobile App to App Store

JavaScript Mastery| 03:43:15|Mar 28, 2026
Chapters15
Introductory overview of bridging web React knowledge to React Native with Expo, outlining the Recurly app project, the stack, and the plan to publish and monetize a mobile app while leveraging free tools and a pro pathway for advanced learning.

A hands-on, production-ready React Native full course from setup to App Store deployment, using Expo, Nativewind, Clerk, PostHog, and AI-assisted workflows.

Summary

JavaScript Mastery’s Adrian walks you through building Recurly, a full-stack subscription app, entirely in React Native with Expo. The course starts by reframing mobile as a web-friendly stack—Expo Router file-based routing, and a modern bridge-free RN architecture—so web know-how translates quickly. You’ll set up a complete stack (Expo, Nativewind, Clerk for auth and billing, PostHog for analytics, and Code Rabbit for AI-assisted code reviews) and then ship with EAS, including cloud builds and app-store submission. Adrian emphasizes a production-oriented workflow: real authentication flows, analytics-in production, and a data-first UI built with FlatList, safe areas, and responsive theming. The project advances from a blank folder to a fully navigable app with onboarding, tabs, dynamic routes, and detail pages, all styled with Tailwind-like class names via Nativewind. He also demonstrates how to structure navigation with Expo Router (route groups, layouts, and dynamic routes) and how to handle safe-area constraints and keyboard interactions in mobile UIs. Throughout, the course leans on AI tooling (Juny/Code Rabbit) to speed up boilerplate, code reviews, and even complex flows like Clerk authentication. Finally, you’ll learn to push to GitHub, track with PostHog, and deploy via EAS, culminating in a production-ready app ready for TestFlight or App Store submission. This is less about a single app and more about mastering a repeatable, production-grade mobile development workflow.

Key Takeaways

  • Expo Router enables file-based routing in React Native, turning screens into routes by simply adding files and folders.
  • EAS (Expo Application Services) handles cloud builds, submissions, and over-the-air updates—no local Mac required for iOS builds.
  • Nativewind provides Tailwind-like styling for RN, with v5 bringing the latest Tailwind features to mobile apps.
  • Clerk manages authentication and billing out of the box, including email verification and session management, with native Expo components in beta.
  • PostHog is used for analytics and feature flags, with both wizard setup and manual event tracking to observe real user behavior.
  • Code Rabbit (AI code reviewer) analyzes PRs, suggests fixes, and can generate diffs or even commit-ready changes to speed up reviews.
  • Routing patterns include route groups, layouts, and dynamic routes (e.g., subscriptions/[id]), plus a tab layout for bottom navigation.

Who Is This For?

Essential viewing for React developers new to mobile who want to build, publish, and monetize full-stack RN apps. Also valuable for teams adopting a production workflow with Expo, EAS, Clerk, and PostHog to ship apps efficiently.

Notable Quotes

"Did you know that if you already know React, you're about 75% of the way to being a mobile app developer?"
Opening remark to set expectations for web-to-mobile transfer.
"Expo is the officially recommended way to build React Native apps."
Justifies the tooling choice and direction of the course.
"EAS build compiles your app in the cloud. No MAC needed for iOS builds."
Highlights the cloud-native deployment advantage.
"We start with a crash course covering React Native fundamentals and Expo's latest features."
Frames the learning path for beginners.
"Code Rabbit flags issues and writes fixes you can push with one click."
Shows how AI-assisted reviews fit into the workflow.

Questions This Video Answers

  • How do I publish a React Native app built with Expo to the App Store and Google Play?
  • What is Expo Router and how does file-based routing compare to traditional RN navigation?
  • Can I use Tailwind-like styling in React Native, and how does Nativewind integrate with RN components?
  • How does Clerk authentication work in a React Native app, and what’s involved in setting it up with Expo?
  • What are the best practices for integrating PostHog analytics in a RN app and using their wizard vs. manual tracking?
React NativeExpoExpo RouterNativewindTailwind CSSClerkPostHogCode RabbitEASSubscriptions app (Recurly)
Full Transcript
Did you know that if you already know React, you're about 75% of the way to being a mobile app developer? Most of what you've learned building websites carries over directly to building mobile applications. The components, the hooks, the state management, it all transfers. But here's what changed. React Native now isn't the React Native from 2 years ago. It's closer to Tanstack Start or Nex.js now. file-based routing, API routes, and server components. The gap between web and mobile has basically closed. Hi, I'm Adrian, and in this course, you're going to build, publish, and monetize a full stack mobile app from scratch all the way to the app store. The app is called Recurly, a subscription management app that helps you track every recurring charge in your life. You'll connect it to a real backend with Node.js, Express, and MongoDB. You'll set up authentication, implement active and inactive subscription tracking, scheduled email reminders so your users never miss a billing date, and a custom tab navigation that feels truly native. But then you're going to do what 99% of tutorials skip. You'll take this app through EAS and publish it to both the Apple App Store and Google Play Store. while at the same time learning how to charge real money for it. Here's the stack. Expo, the official framework recommended by the React Native team. Native wind to style your React Native apps using Tailwind CSS. Clerk for O and billing. Code rabbit for code reviews. And Posthog for analytics. We start with a crash course covering React Native fundamentals and Expo's latest features. Then we build and then we ship. And to make your journey even easier, I put together a free React Native road map that covers the essentials you need to know and the exact path to getting hired as a mobile developer today. You can get it from the video kit link down in the description. By the end, you won't just know React Native. And you won't just have built a mobile app, but you'll have published one to the app store that people can actually download and pay for. Look, if you're serious about leveling up from watching tutorials to actually becoming higher ready, you'll love JSMy Pro. It's where I go beyond the build this app stuff and teach the engineering mindset behind the code. Inside Pro, you get full premium courses like the ultimate Nex.js, testing, animations, JavaScript, SQL, 3JS, and more. Quizzes after every lesson, so you actually lock in what you learn. interview practice with our AI interviewer, so you can train for real technical interviews the same way you train for a sport. And we're also launching the ultimate back-end course and our AI engineering courses next. And our pro members get early access. You also get access to our private Discord where you can ask questions to real human beings and get help fast. If you want to check it out, I might give you a special discount just because you're coming from this video. Give it a shot. The link is in the description. Before React Native, building mobile apps meant maintaining two completely separate code bases. One for iOS in Swift and one for Android in Cotlin. That's double the work, double the bugs, and double the cost. For most teams and especially individual developers, this just wasn't realistic. and React Native change that one codebase running across both platforms. You write plain old JavaScript and React and it renders actual native components on each platform, not just a web view or a hybrid wrapper, real native UI, which on iOS devices means Apple's latest liquid glass. Unpopular opinion, I think it actually looks pretty good. And React Native is huge. Companies like Meta, Microsoft, Discord, Tesla, Amazon, Coinbase, Shopify, and even Call of Duty all use React Native in production, but the old criticism was always performance. React Native apps feel sluggish. Well, that was a fair point 3 years ago, but not anymore. The new architecture, which shipped as the new default, replaced the old bridge system entirely. JavaScript now talks directly to native code through something called JSI which means that modules load on demand instead of loading at startup and the rendering layer syncs with React's own update cycle. In short, React Native apps are now as fast as fully native ones. So, that debate is over. Oh, and on top of that, you also get hot reloading for instant feedback while developing a massive ecosystem of community libraries. And if you already know React, you can pick this up in a day. That's what I'll prove to you. As you know, React Native is a runtime, but we'll build with Expo. If you've used Nex.js with React or Next with Vue or Tanstack Start, you already understand the concept. Expo is the framework layer on top of React Native. It handles your dev environment, your routing, your builds, and your deployments. React Native gives you the engine, and Expo gives you the car. And before anyone in the comments says, "Real developers don't use Expo." Well, check React Native's own documentation, Expo is the officially recommended way to build React Native apps. That's not my opinion. That's the React Native Steam's recommendation. So, why does this matter to you? Well, there's a few reasons. First, no Android Studio or Xcode required to get started. You just run one command and you have a working app on your phone in under 2 minutes. Still, within this course, I'll teach you how to set up Android Studio and Xcode in order to be able to publish your app to real app stores so other users can download them and even pay for the features within them. Second, Expo handles native dependencies for you. Camera, apps, notifications, biometrics, all pre-built and tested across both platforms. Third, file-based routing. If you've used NexJS, you already know how this works. You create a file and it becomes a screen. Nested folders become nested routes and dynamic segments start with square brackets. It's the same mental model. And fourth, Expo will give you the easiest path from making the app run on your phone all the way to making the app live on the App Store. You don't need to switch tools halfway. That path is called EAS, Expo Application Services. Think of it this way. Expo helps you build your app and EAS helps you ship it. EAS build compiles your app in the cloud. No MAC needed for iOS builds. EAS submit sends it to the App Store or Google Play with one command and EAS update lets you push fixes directly to users devices without going through store review. This is huge. It's like refreshing a website but for your mobile app. There's also EAS workflows to automatically build, sign, and submit your app directly from a GitHub repo. And then there's Expo Observe to measure real user metrics like launch time and app responsiveness. With this, you can fully automate your release process and track exactly how your app performs for real users in production. And companies like Discord, Coinbase, and Xbox use EAS to ship their production apps. Of course, it's one thing to just mention all of these features, but throughout this course, you'll get hands-on with all of this a bit later when we actually deploy Recurly, our subscription management app that you'll build. So, for now, just know that the tooling exists and it's solid. So, that's your development and deployment stack. But in this course, I want to take you beyond just it works in my machine. You're going to be setting up the same tools that real companies use to authenticate users, track behavior, and catch bugs before your users do. And here's what makes this even better. Every single tool we'll use in this course is completely free to get started with. No credit card required. You can build, ship, and monitor a real app without spending a dollar. So, let me walk you through what we're using and why. First up, Clerk for authentication and billing. If you watched any of my previous courses, you already know Clerk. It handles signup, signin, user management, and session handling with just a couple of lines of code. What used to take me days of wiring up odd providers, managing tokens, and building login screens now literally takes minutes. For recurly, we'll use Clerk's custom oflow with Expo. You'll set up secure authentication, manage user sessions, and handle the entire sign-in experience. Oh, and Clerk just launched native components for Expo, which is essentially pre-built UI that renders using Swift UI on iOS and Jetack Compose on Android. We won't use them directly in this build since they require dev builds to keep the setup as simple as possible and to teach you as much as possible in the shortest amount of time, but they're totally worth exploring once you're comfortable with the fundamentals. And on top of O, we'll also use Clerk for billing. You learn how to set up subscription plans, manage payments, and enforce plan limits. This is super cool because it'll actually allow you to make money off of the apps that you build and publish to the App Store. So go ahead and create a free clerk account now. The link is in the description and you'll need this account when we get to the o section of the build. Now post hog for analytics and exploration. Building this app is only half the job because once it's live, you need to know what your users are actually doing. Which screens do they visit? Where do they drop off? And are the push notification reminders actually bringing people back? Postthog answers all of that. We'll integrate their React Native SDK and use it as our single tool for understanding user behavior. You'll set up event tracking to see which subscriptions users add most, funnel analysis to measure how many users complete on boarding, feature flags to roll out new features to only a percentage of users before going live, and air tracking to catch crashes the moment they happen. Post Hog also gives you session replay on mobile, so you can literally watch how a user navigates your app and see exactly where they get stuck. And if you want direct feedback, you can trigger in-app surveys after key moments, like asking users what subscription service they wish you supported. Post Hog's free tier is generous, more than enough for what we're building. So, create your account from the link in the description so it's ready when we get to the analytics section. And we'll also use Code Rabbit for AI powered code reviews. When you push code to GitHub, Code Rabbit automatically reviews every pull request. It reads the diff, understands the context of your entire codebase, and leaves line by line feedback, security issues, edge cases, and things that are easy to miss when you're building fast. But it doesn't just tell you what's wrong. It also suggests fixes you can apply with one click, right from the PR. Think of it as having a senior engineer reviewing every commit, except it's instant. And another huge part I love about Code Rabbit, but they don't even advertise it as a feature, is that you can learn so much from it. For our project, you'll connect Code Rabbit to your GitHub repo and see it in action as we build recurly. And as you're building this React Native application, it'll actually provide you with advice on how you can improve your codebase, write more quality code, and just understand React Native and features your implementing much more deeply on a fundamental level. It's a two-click setup, free for open- source, and has a free tier for private repos. Same as others, create your account right now with a link in the description. So, here's the full picture. Expo and EAS handle your development and deployment. Clerk handles off and billing. Posthog handles analytics and experimentation. And Code Rabbit handles code quality. That's a production stack. The same categories of tools that the biggest companies use to ship their mobile apps. And every single one of them is free for you to start with today. But before we plug any of these in, you need to understand React Native itself. how the components work, how styling works, how routing works, because that's the foundation everything else builds on. So, let's dive into the crash course. All right, let's jump right in. We've already talked about the benefits and drawbacks of React Native and how Expo can make your life easier. Now, it's time to dive into the code and see how it works. If you've worked with ReactJS before, you'll find that React Native uses a similar syntax. But of course, there are some differences you should be aware of. So, let me show you the ins and outs of React Native code. We can check out how it looks and how it functions when compared to ReactJS to help you understand the similarities, but also the differences between the two. So, super quickly, you'll understand React Native's components and how to use them. When coding in React Native, you use JavaScript just like with React, but instead of rendering HTML elements, you'll be rendering native mobile components. Take a look at this basic React Native component. Here we're importing two important components from React Native library, view and text. Then we create a functional component called app that returns the text component wrapped inside of a view. What's interesting here is that we're using JSX syntax, which makes it super easy to create and visualize our components in a more HTML-ike way. But if you take a closer look, you can quickly see that this is neither a P tag nor an H tag, nor anything that we're used to while writing code that runs in the browser. In React Native, we use text instead. It's pretty straightforward. The text component is used to display text in the app and you can style it using the same CSS-l like syntax as in React. You can set the font size, color, and weight using the style prop. React Native also offers a stylesheet utility that allows you to define styles by creating a single JavaScript object. This is super handy in larger applications as it optimizes performance. But as we all know, Tailwind CSS is rising in popularity. So in React Native world, Nativewind came into the picture, allowing you to write Tailwindl like CSS styles in React Native. Isn't this crazy? It feels like you're writing a regular web app, but instead you're developing apps for mobile. Now, let's talk a bit about the view component. Think of it as a box or container that holds other components. Similar to the development in HTML, but with some added functionality specific to mobile apps. The view component is often used to create layout structures for other It has many different props that can be used to control its appearance and behavior. And one thing to note is that the view component uses flexbox layout by default, which makes it really easy to control how its children components are laid out. So, you can use flexbox properties like flex direction, justify content, and align items to achieve any layout you want. But what if you want to add some interactivity to your React Native apps? Well, get excited because there are some amazing components that do just that. Components for creating buttons, links, and other interactive elements. The first one on the list is touchable opacity, which is great if you want to create a simple button. Think of it like a cousin to a React button component, but with even more room for customization. And instead of an on click in React Native, you're not clicking it, you're pressing it. So, let's provide an on press. The second similar component is called a touchable highlight, which allows views to respond to touch in a unique way. When touched, the component reduces the opacity of the wrapped div, revealing the underlying color. And then there's the touchable without feedback, which is there if you need to create an element that is clickable, but you don't want it to have any visual feedback when pressed. It is super useful when creating links or images that don't need any additional effects. And apart from these touchable components, there's also activity indicator, which allows you to show a spinner or a loading indicator within your app. Sure, there's also a simple button allowing you to set properties like the title, color, and an on press that is called when the button is pressed, but whenever you need some more advanced styling or behavior, you'll find yourself using the touchable components much more often as they offer greater flexibility. Now, the next super important component on the list of components I want to teach you right now and which we'll use later on within our app is called a flat list. A flat list is perfect for rendering long lists of items that need to be scrolled. It's like the map function in React, but with some extra features like optimized scroll performance and item separation. The way it works is you of course import it from React Native and then you define some data such as an array of objects you want to map over. Then you put it all in a view and you call it flat list. A flat list by default already accepts some props such as the data prop to which you can pass an array of data you want to map over and then a render item prop which allows you to define exactly how you want to represent each item in the array. Pretty cool, right? Now, when should you use a flat list and when should you just map over the elements? Well, for larger lists where you want to have smooth scrolling, go for the flat list. While for the smaller lists, the map function will do the job. There's also something known as a scroll view. You can think of it like a magic box that can hold multiple components and views, providing you a scrolling container for them. It's like the Orflow scroll property of a div in HTML, allowing you to easily navigate through a list of items or large amounts of content in our app. You can put it within a view, call it scroll view, and then render some elements within it. By using the scroll view component, you can make sure that users can easily explore all the content, making your app more intuitive. And there's also a component which you'll find yourself using super often called safe area view, which gives you some sort of a safe zone to render your app's content within without it being covered by the devices hardware features like the notch, home indicator, or a status bar. It's great for building apps that are supported on different devices with different screen sizes and shapes. The way you use it is whenever you think something might be too long or awkwardly placed, you just wrap it within a safe area view. And while that default safe area view is amazing for most cases, sometimes it does fall short for some devices, making it not the optimal choice. So, I often like to use a package called React Native Safe Area Context, which works across all devices, even for the bottom bar. But you don't have to worry about remembering all of that right now. I'll show it to you once you start developing your own first app. But bear with me for a moment. I first want you to know which components exist so we can later on use them more easily. For example, we'll surely have to display some images within our app, right? So, how can we do that in React Native? Well, thankfully once again, it is super similar to how it looks like in React. You just use the image component provided by React Native. You pass the source to it, which can be a path or a URL, and you're good. But if you want to display an image as a background, then you should use the image background component, which works in the same way, but just renders it as a background. is specifically designed to allow other components to be layered on top of it. Whereas just the image is used for displaying images on their own. And both of these can handle different image formats like PGs, JPEGs, GIFs, and WEBPs. But none support SVG files because of some native rendering limitations. So if you want to use SVGs, there's a third party package called React Native SVG. Once again, don't try to remember it. I'll show it to you once again later on so you can use it within your own code. Okay, now that you know how to use images, what about modals? Well, thankfully React Native has components for that, too. It's called a model. Just import it from React Native, set visible to true, add an animation type, and define what happens when you close it. Pretty simple. There's also an alert component which you can again just very conveniently import from React Native and call it by saying alert. Provide a title and functions which you want to execute on cancel or on okay. And if you're creating forms you might want to create some kind of a toggle in React Native. And for that you can use a switch component. Once again it is super simple. Import it. Create a state for it. Same as in React. create a function that manages it and then simply call it like this within your code. Provide some colors, its value, and what happens when you click it. There's also a component that I use super often called the status bar. Both React Native and Expo have their own versions that allow you to control how status bar should look like for each screen within the app. I prefer using the one from Expo. You can define a view with some text and just beneath it, you can define a status bar. But what am I even trying to do? I'm just listing all of the different components that you can use to build your mobile applications. Well, I guess I'm just trying to show you that there are some differences like P tags and H tags are now becoming text. Divs are becoming views. But other than that, there are so many components that feel like you're writing React code within React Native, allowing you to build mobile applications. from touchable opacities for buttons to modals, alerts, and more. The list doesn't stop there. Of course, I don't want to waste your time. We're not going to go through every single component that exists in this video, but I did take some time and I wrote down the most important ones within the free React Native guide linked in the description. So, if you want to check them out there, see the differences and similarities, and just have a place where you can recap what you learned, that guide is what you need. But now that you know the most important components and concepts in React Native, let's dive right into action. I'll show you how to set up your app, what files and folders are involved, and everything else that matters step by step. All right, you've got the fundamentals down. You understand how React Native works, how Expo simplifies everything, and what tools we're using to make this production ready. Now it's time to build recurly. And I'm not just going to show you how to build this one app. I'm going to teach you the full workflow from an empty folder to a published app on the app store so that you can use this process for any mobile application you want to build in the future. Once you understand this, you can ship anything. So let's start at the main source. ReactNative.dev. This is the official React Native documentation maintained by Meta. Head over to get started and you'll see something important right at the top. React Native allows developers who know React to create native apps. And then it says, we believe that the best way to experience React Native is through a framework. And then you can also use React Native without a framework. However, we found that most developers benefit from using a React Native framework like Expo. That's the React Native team telling you directly, "Use Expo. It provides file-based routing, highquality libraries, and plugins that let you modify native code without managing native files yourself." So, that's exactly what we'll do. Head over to expo.dev. Expo is a full stack React Native framework with cloud services that cover every stage of the app cycle. Development, deployment, monitoring, and everything. Take a look at who's using it in production. There's Discord, Coinbase, Xbox, Burger King, and hundreds more. These aren't side projects. These are apps serving millions of users. It gives you tons of productionready libraries. camera, push notifications, biometrics, all tested across both platforms, so you don't have to fight with native modules. The developer experience here is built around speed. You develop directly on your phone using Expo Go. You can launch emulators without opening Android Studio or Xcode. Still, I'll teach you how to do that later on as it's needed to actually put this project up on both the app stores. So, before we write any code, let's get your Expo account set up. Click get started for free and create your account. It's completely free with no credit card required. Once you've set it up, we can create a new starter project, which will lead us to the instructions on how to do that. But you don't have to follow the steps here, at least not right now, because I'm going to walk you through the entire process step by step, starting all the way from creating an empty folder on your desktop and opening it up within your ID of choice. In this case, I'll be using WebStorm. So, simply drag and drop that empty folder in and we are ready to get started. If you want to follow along and see exactly what I'm seeing, I would recommend that you also download WebStorm as your IDE, as as of recently it became completely free for non-commercial use, which means that you're getting a professional JavaScript and TypeScript IDE well for free. And with it, you can also set up Juni, your smart coding agent. We'll use it throughout this course to speed up our development. Not the fundamental parts where I want to teach you a lot of stuff about how React Native works, but the repetitive stuff, the stuff that just takes time and is boring to implement. Anyways, so you can go ahead and set up WebStorm and Juny right away. Once you're within your text editor or IDE, you can open up your integrated terminal and let's create our Expo application by running mpx create-expo@ latest-template. And we're going to use default at SDK-54. Now, why 54 when 55 is already out and Expo is going to add incremental upgrades to 56, 7, 8, and so on? While these upgrades are useful and they bring in some new features, what I care more about is for you to have the best experience following along with this course. I want you to learn as much as possible without running into any kind of version related obstacles. So follow along like this and then you can easily upgrade once you build the application and then add the dot slash at the end to create the app within the current folder. Press enter. It'll ask you whether you want to install the create expo app installer. So press Y to install it and it'll download and extract the project files and start setting up the project structure for you. So let's wait until it finishes. In less than a minute, our project is ready. And you can run either one of these three commands to run the project. Or you can head back over to the Expo website where we started a new project. And here you can see the second step, and that is to download the Expo Go application. And only after we have it, we'll run MPX Expo Start, allowing us to scan the QR code and connect to our computer. So, this is a great point for me to display my device right here on the right side. So, at any point in time later on throughout this course, you can see what I'm seeing directly on the phone that I'm holding in my hand. First things first, scan this QR code and download the Expo application for your phone. Then, back within the terminal, run MPX Expo Start. This will fire up a Metro bundler. It'll give you some kind of a QR code right here. and also additional options and shortcuts you can press. You can press the letter A to open in Android Studio, I for iOS simulator, R to reload, J to open up the debugger, and we'll use a few of these later throughout the build. But like I said, you don't need Android Studio or Xcode to get started because the easiest way to develop is with Expo Go on your phone. So once you download it, if you're on iOS, head over to the camera app and simply scan the QR code and press open in Expo Go. This will immediately run your application directly on your phone. And you can see it right here. This is the developer menu. You can shake your device or long press anywhere on the screen with three fingers to go back to it at any point in time. And then you can see all the additional settings. So once again, if you want to visit it, just shake your phone in your hand, literally, or press and hold with three fingers and it'll open up. And that's it. We have a fully functional mobile app running on our phone in a couple of seconds. If you're on Android, you'll have to scan the QR code directly from the Expo Go app. And if you're on the iOS and it's not connecting, make sure that you're on the same network as your computer and that your VPN in settings is turned off. So, go to settings, search for VPN, and make sure that it says not connected. And also make sure that you're connected to the same Wi-Fi network as your computer. And if you do that, it should all work. And what you have here is your first React Native app running on a real device, which took about 2 minutes. But now, let's understand what Expo generated for us. I'll go from top to bottom and cover the most important file. First, we have the package.json, which is your project's manifest. The key line here is the main part where it says expo router entry. This tells Expo to boot the app through the Expo router filebased routing instead of manually written app.dsx. Then you have your scripts right here, starting with the start script. Then you have scripts for Android, iOS, web, lint, and finally the reset project script which we'll use in a moment. Then there's the app.json file which is expose configuration file. It controls your app name, icon, splash screen, orientation and also platform specific settings for iOS and Android. We'll come back to this later on when we configure our app assets and set up EAS for deployment. For now, you just need to know what it does. There's also the tsconfig.json file right here at the bottom. This one extends the expose base TypeScript config. And two things matter here. Strict is enabled which catches more bugs at compile time and the path aliases that we said so that we can import with add slash instead of using the full relative path like dot dot slash dot dot slash and so on. So this is going to be super handy. Then there's the package log JSON which locks exact dependency version.get ignore which keeps node modules expo cache and build artifacts out of the version control. There's also the scripts folder which has utility scripts within them. The assets folder which holds your images, icons, splash screens and fonts. And now we dive to the most important parts. The real code lives within your app folder. Within it, you can see all of the different routes. First, there's the underscore layout file, which is the root layout that wraps all of your screens. Right now, the only thing it does is it renders the tab navigation with two tabs that you can see at the bottom, which changes the screen. Then there's the index.tsx, which is the first thing users see when they go to the forward/ route. There's also the second page, which is the explore page. If you click it on the tab navigation at the bottom and also under components, you'll be able to see all the components and reusable pieces of UI like the animated pieces of text, scroll views, and more. But that's a lot of starter code that we don't need. We're building things from scratch. So, let's clean the house. Open up your terminal. And I'll actually open up a second one. This one I'll call expo. And a new one, which I'll call simply terminal. And within it, I'll run mpm run reset project, which is one of the scripts within their package.json that Expo created for us. This script will ask you whether you want to remove the existing code or you want to move it to an example folder instead. I'll simply move it so you can refer to it later on by pressing Y. Expo Go would now like to access your motion and fitness activity, which I'll give it access to. And the project is reset. The next steps are to rerun the MPX expo start. So I'll simply stop it from running in the initial terminal by pressing Ctrl + C and then run MPX expo start. This will simply restart it. And again on your phone you can hold it with three fingers at the same time and then tap reload. As soon as you do that you'll see that now we have a fully empty version of our application that simply says edit the app index DSX to edit this screen. And there is absolutely nothing else here. So this is our starting point and everything we build from here is recurly your full stack subscription management application. Next let's set up styling. We're using Nativewind which lets you write Tailwind CSS classes directly in React Native. So if you've used Tailwind on the web, you already know how to style mobile apps too. same utility classes and same mental model. So head over to nativewind.dev and we'll be using the latest version of nativewind v5 which uses the latest Tailwind CSS features. It might still be in pre-release by the time you're watching this, but the core principles, the classes, and the workflows are stable. If anything changes, it'll be minor file paths or configs, not the way you write styles. So head over to get started. And here you can see the installation steps. But instead of following this v4 version, we're going to switch over to native wind v5, which is the latest version. And then you can go to the installation page and follow these steps right here. If we were starting from a new project, you could have automatically set up a new expo project with native windv5, expo 54, and tailwind CSS. But since we have an existing project, let's do it step by step. I'll put my terminal right here on the right side so that we can more easily go through the steps. The first step is to install native wind and its peer dependencies. Tailwind CSS, React Native CSS, React Native Reanimated and React Native Safe Area View. And we can do that either with Expo or through MPM. In this case, let's proceed with Expo by running mpx expo install and then install all of these additional dependencies which are essentially going to do the same thing. Simply run mpm install and add them. There we go. Now moving on to the second step which is to set up Tailwind CSS. We'll copy and paste this command. Optionally we can also install prettier plug-in Tailwind as a dev dependencies so we can format our code. And we need to add Tailwind to our post CSS config. So we need to create a new postcss.config.mjs which I'll do right here in the root of our application. Create a new file called postcss.config.mjs. We can automatically add it to git. And then we can simply copy and paste this block of code within it. We also need to create a globals.css CSS file or global.css and add the following tailing directives. We already have the global file. No, I thought we had but we don't yet. So, let's create a new file called and simply paste these four lines right into it. From here onward, replace global with the relative path to the CSS file you just created. Okay, so that is this global.css file. Now we need to create or modify our metro config. I don't see that we have that metro config yet. So we can run this command right here. That's going to be mpx expo customize metro.config.js to create that file. Soon enough it'll be generated and we'll be able to modify it. So simply we can replace it with what we can copy over from here in native wind. And then we can also import our CSS file within either our app or within our layout. So if you head over into our app layout.tsx here, we're importing everything. So it might be a good idea to just import the at /global.css file right here for the styles to take effect. We also need to force lightning CSS to a specific version in our package JSON. So let's copy this. head over into the package.json file and under overrides which are going to be at the bottom or we can add them right here below the dev dependencies. We're going to set the lightning CSS to 131 as they specify right here for you. These steps might be a bit different but it's important that you follow along with exactly what they have. And we are using TypeScript. So we have to create a new file called native wind- env.d.ts also at the root of the application. That's going to be native wind- env.ds. And we have to just add this reference right here. This file should not be edited and should be committed with your source code. Finally, we are ready to try it out by copying this updated app.tsx. Moving over to our app index.tsx and replicating everything we had with this new welcome to native wind part. It should show how class name props can be used tailwind utility classes color utilities and also typographies. And if you see the style text centered on a white background, native width is working properly. So let's test it out by bringing it right here. Immediately you'll see that the changes are not yet applied which is fine. That means that we have to restart our application. So I will simply press the letter R to do a manual reload first and then we'll get a little error saying that it cannot find the global.css file. So let's just fix this path by pointing to at/global.css and then run r to reload one more time. And now we can see welcome to native wind. Although the styles are not applied, this text isn't centered in the middle of the view, and we don't have a background or a blue text. So, what I'm going to do instead is stop the server by pressing command C, and then rerun it again with MPX Expo Start and then press the letter R in the terminal to rerun it one more time. This issue is most likely connected to, yep, I think I found it. Lightning CSS, which is one of those uh version mismatches that the docs warned us about. We added this override for it right here within package.json at the bottom, but we never reinstalled after adding it. The override only takes effect on a fresh install. So, what we need to do is delete the node modules as well as the package lock JSON and then reinstall. I'll do that by opening up the second terminal and running rm-rf which means delete folder and file node modules and package lock json or you can delete them by right-clicking and then clicking delete. Once that is done, simply run mpm install one more time to reinstall our dependencies. Let's let that run. And after that is done, simply head back over to Expo, stop it from running and run MPX expo start with a d-clear flag, which will restart Metro with a clear cache. And then press enter. And then finally, the letter R to reload the app. As soon as it rebuilds it, you'll be able to see a blank screen with a center text that says, "Welcome to Native Wind." exactly as we expected. Now, currently we have a blank slate and we can use all of these Tailwind utility classes which already makes the development of this app so much simpler because we don't have to have separate CSS files and so on. But one way to improve it even more styling wise is to form some kind of a theme for our application. For that reason, in the video kit link down in the description, I'll share the access to the subscription management app design on Figma. It looks something like this. It has very bold colors and allows you to budget a bit better. So, from here, we would need to extract specific colors like this huge primary color E87A53 orang-ish. Then, we have this papery like background color right here. and then some accent colors too. Instead of extracting all of these manually, in that same video kit link down in the description, I'll share with you the final global.css file. It'll include this tail and CSS imports, but also a couple of components for the tabs and some spacings so we can have uniform space within our application. And most importantly, the color, the background, the foreground, even some primary colors, the accent, the one that we saw, and colors for success, destruction, and subscription. Perfect. This is it. So, simply copy and paste that file here. In simple words, this is basically Recurly's design system. So, let's test it out by heading back over within our index.tsx. And we can now try using these custom color codes. For example, instead of BG white, I'll set it to BG background. And instead of text-b blue 600, we'll set it to text- success. So now if you save this, you can see that everything changes instantly, which means that we have successfully set up and installed native wind, tailwind utilities, and a custom color system that matches our Figma design. So from here on, every component we build will use these variables. Nice. So now when you start creating your new applications in the future, you know how to style them, too. Oh, and a pro tip. If you're starting with a new project and you already know that you're going to be using native wind from the start, you don't have to follow all of these steps. You can just install and set up everything from a single command right here. With that in mind, the styling setup is done. And in the next lesson, we can dive right into the routing and navigation of our application. As I mentioned in the crash course, routing in React Native with Expo Router works almost identically to Nex.js. The file name becomes the route path. No manual navigation config, no route arrays, and no wiring together by hand. So, in this app, every file inside of the app folder will become part of the navigation tree. But unlike a single demo app, our root route isn't the full home screen. It's an entry point that decides where the user goes. So, back within our application, we have this app index.tsx, the starting route that's already set up. Now, let's prove how easy it is to add a new one. Say we want to add an onboarding screen. Simply create a new file right here called onboarding.tsx. Run rf, which is a React native function with export. And this will quickly spin up a new functional component with a view and a text. That's it. Your onboarding screen now exists at the forward/ onboarding path out of the box. To test it, you can go back to your homepage and add a link. I'll do that right here by saying link, which we can import from expo router with an href pointing to onboarding. And we can also give it a class name set to margin top of four rounded bg primary text- white and P4. It's going to be like a button that'll say go to on boarding. If you save it, you'll be able to see it right below. And if you press it on your phone, you'll be navigated over to the onboarding. And the best part is you can use your devices native capabilities to go back. Since I'm on an iPhone, it's natural for me to just scroll from left to right to go back. That's the core idea of Expo Router. create a file, it becomes a route, and you navigate to it with a link. Same pattern you'd use in Nex.js. But now, let's think about this in terms of a real application. Open up the Figma design. As you can see, we've got onboarding screens. There also might be some O screens and then main app screens with tabs and detail pages. If we throw all of these files directly inside of the app, the project will get messy pretty fast, 10, 15 files all at the same level with no organization. Expo Router solves this with route groups. A route group is a folder wrapped in parenthesis, which lets you organize related screens into folders without adding that folder name to the URL. So let me show you how to implement it for authentication. We'll start by creating a new directory in the app folder and start with parenthesis and end with a closing parenthesis and call it O. Then within O create two new files sign-in.tsx tsx where you can run rf and this one will be called sign in and it'll have a link component which is going to be pointing to o like this. So forward slash route group and then forward slash signup and this link will say create account. Now I will copy this entire page and create another file which is going to be called I think you can guess it sign dashup.tsx where you can simply paste it and rename this from sign in to sign up. Change this text right here to sign up and then instead of create an account you can simply say sign in. And because O folder is wrapped in parenthesis, the word O will never appear in the URL. Well, if you were in the browser, uh these routes will resolve directly to sign in and signup pages, not O/signin. These parentheses are purely for organization. The URL stays clean. For now, these are placeholder screens as we're learning the navigation structure, not building the UI yet. But let's just verify they work by updating our homepage and adding two new links. I'll create one right here. And actually, I'll just go ahead and duplicate the existing one we have two times. And this one will say go to signin pointing toward slash off in parenthesis slash signin. And then the second one will go to sign up and it'll simply say go to sign up. Perfect. So now if you save it, you'll be able to see three buttons. So if you click on sign in, you'll see unmatched route page could not be found. So what we can do is reload the page and try to go to sign in. And now you can see that it works. This is a another pro tip that you might encounter needing often and that is that even though your code is 100% correct and everything should be working, either it's going to show you an error or the right thing isn't going to show up on the screen yet. Um, and when that happens, you just need to press the letter R in your keyboard to reload it or hold three fingers on your device and then reload from here. Either way is totally fine. And sometimes there's going to be cases where even that is not going to fix it. So then you'll have to press Ctrl + C and restart the entire server, maybe with a clear flag to clear the cache. But more on that later. For now, we have three fully functional routes. And you also learned about the concept of route groups, which we can use for organization. But then we also have something known as layouts. And the concept of a layout is directly connected to the concept of a group. For example, if I go to onboarding, you'll see that it'll just say onboarding at the top because it is at the root route. But if I go to signin, you'll see o and then sign in. That's because expo router uses stack navigation by default and each layout adds its own header. So just like this root layout wraps all the routes, we can also create a layout specifically for the o group. So head over to the O and create a new file called underscore layout.tsx. Within it, we can practically duplicate this existing layout with a few modifications. Instead of returning the stack empty, we'll return it with an additional screen options property where we'll set the header shown to false. And as soon as you do this and head over into signin, we were hoping to see the header completely hidden. But that isn't really in the case. What is the case is that without it, you would have two different headers. The one from the homepage and then the one coming from the second layout. So this one only hides one, the header for the odd screens. But you'll still see the root layouts header on top. We can hide that too by also applying the same property right here of screen options shown set to false. And now we get a nice blank screen. Very clean. The important takeaway here is that every screen inside of the O now shares this O layout. Later when we build real O UI, this layout is where we'll add shared styling, background colors, or animations that apply to all O screens. And finally, let's talk about tabs, specifically bottom navigation. If you check the design, you'll see that we have a couple of tabs right here at the bottom. One is pointing to the homepage. Then there's the my subscriptions page. Then there are some additional insights and then optional settings. These four screens need bottom tab navigation. So back within our code, let's create another route group. Route group is simply a folder wrapped in parenthesis. So that's going to be called tabs like this. And it's going to be within the app folder, not within the O folder. Then inside it, create a new file called subscriptions.tsx tsx where you can run rnf to quickly spin that component up and then we'll create another one which is going to be called insights.tsx tsx also run rnf and finally also add the settings page. So right here settings dot tsx and run rnf. This gives us the three pages that we'll later on implement. And don't forget that we also have the home screen which is our Also move it inside of the tabs folder. It'll still function as the homepage since index.tsx tsx always maps to the root of its group. So again, since we don't have an index here, it's going to take the one from here, but now it's part of the tab structure. So this is a super useful routing pattern. You've got your app level entry point at the root, which is this layout right here, and then your o screens are grouped together because in most situations, they don't need the bottom navigation. But then your main application screens are grouped under tabs so that each group can have its own layout, its own navigation behavior, and its own shared UI all organized within folders. Oh, and one more thing, there are also these so-called dynamic routes. So, if you need a subscription details screen, you don't want the tab bar showing that on the page because there's going to be hundreds of different subscriptions and we can have a tab button for each one. Instead, we want to create it outside of the tabs group as a dynamic route. So, head over into tabs where we have subscriptions and create a new folder of subscriptions this time. And within the subscriptions folder, create a new file wrapped in square brackets and make it called ID like this. but ID ending and tsx because that's also its own page. I'm not quite sure why there's a snail here, but I'll take it. Maybe if I recreed, it's not going to show a snail. Nope, the snail is still there. That's totally okay. So, the bracket syntax works in the same way like in Nex.js. So, if somebody goes to subscriptions and then a specific number, this component will show. So, let's quickly spin it up by running. We'll call this one subscription details. And don't forget to export it at the bottom. And here the text can say also subscription details. But the question is for which ID. How can we figure out which ID is this user checking out? How do we get this ID variable? It's super simple. Once again, you just dstructure the ID coming from use local search params coming from expo router. And you can even define its type such as an ID of a type string. In this case, that's going to be like this. And I'll also add another link component right here to go back to the previous page. So that's going to be href pointing to the homepage. And it'll say go back. Now we can add these links to test from the homepage. So heading back over to our tabs index.tsx, tsx. Right below these links, we can now add two more. One link pointing to a forward slashsubscriptions/spotify which is going to be for the Spotify subscription. And in the other one, you can do it dynamically like this pointing to a path name and then passing the param of ID like this. Both of these are going to be valid. So now if you reload the app, we're going to be back at the homepage. And if you click on Spotify subscription, right at the top, maybe barely, you'll be able to see a go back button and then a Spotify subscription, which means that it's loading it. But hey, why is all of our text flowing up? This is the most common issue in React Native applications. And that is making sure to contain all of the contents of the application within the specific devices boundary. For example, here on iPhone, the time is shown on top left. And then there's this dynamic island as well. So, we want to make sure to use a safe area view to contain all the content within the safe boundary. I'll teach you how to do that very soon. But yeah, take a moment to think about what you've just built. Four different concepts that power the entire navigation of this app. Starting with files to create routes. You create a file and it becomes a screen. Route groups to organize screens without changing the URL and parenthesis to keep things clean. Then layouts that let multiple screens share the same navigation parent. Basically, every screen in a group inherits its layout. Then there's the bottom tabs navigation, the dynamic routes that let one file handle many detail pages. Don't forget to use the bracket syntax in the file and then the navigation links to actually move between pages. So you're not building a massive navigation configuration and then attaching screens to it. You're building the navigation tree directly through folders and files. And that's the real power of Expo Router. At this point, I think this codebase deserves to be pushed over to GitHub. It's something you should get into the habit of doing as early as possible as the application grows so that you can safely implement new features and go back if you need to. So, let's go ahead and push it by first creating a new repository on GitHub. You can do that by heading over to github.com/new. I'll add it to my own profile and call it react native recurly which is the name of our application. Then simply create a new repo and we can follow the steps to push it. You can either do that manually by following all the steps or you can copy it and we can outsource this work to an AI agent. You can use any AI agent you prefer, but in this case, I'll open up Juny powered by Jet Brains's AI chat, which allows you to complete code, generate commit messages, generate tests, ask questions, and so on. And you can even use it to get explanations of specific code, which is super useful as you're following along with this tutorial. If I don't explicitly explain something, you can use it to give you the explanations so you can learn better. But yeah, it allows you to choose any AI agent out there. I'll set it to auto and turn on the brave mode, which allows the agents to execute terminal commands without confirmation. And then I will simply paste this part right here and press enter. It should now go through all of these commands and push the code over for me to GitHub. It's going to do it command by command and it's going to even write nice commit messages. And there we go. in about what like 5 seconds if you reload your GitHub. Again, you could have totally manually went through all these steps to also push the code. Now, when we're here, I like to do a bit of a cleanup. Uh we can give this project a bit of a description. I'll say recurly subscription management React Native application. For the website, I'll enter jsmastery.com for now. And for the topics, we'll do React Native as well as expo. And we don't need to show these on the homepage. that's going to make your repo that much cleaner and allow us to push new features more consistently. Now, one of the primary reasons that makes me push to GitHub early on nowadays is the fact that we can then get Code Rabbit to analyze our code. Code Rabbit is an AI code reviewer that reviews your code and flags any issues. So, it's super useful to have it early on so that if something goes wrong, we can fix it. It also has a hidden use case and that is that it teaches you how to write better senior like developer code because it flags things on a senior level so that when you fix it once the next time you're not going to make that mistake in the first place which means that you're going to be that much better of a developer. So click the link down in the description try it for free and connect over with GitHub. It'll load up your workspace and then you'll have to connect it by heading over to add repositories and connect over with your GitHub. Once you do, give it access to your repos. And then if you switch to your right account and search for the name of your repo, you should be able to find it right here. And you can make some changes to how it reviews the code. But I don't personally change anything here because it reviews it well by default. So since this was just a demo PR, there's nothing to review. But from here onward, whenever we develop a specific feature, we'll do it how it needs to be done in production, which means that we'll open up a pull request for it, review it, and only if it's good, merge it to main. Let's do that immediately in the next lesson, starting with removing these fake links and turning them into a bottom tab navigation. In the last lesson, you created the route files for Recurly and grouped the main screens inside of the tabs group. That means that you already have the homepage, the subscriptions page, the insights page, and the settings page. Now, we need to connect them with a bottom tab navigation. Just like we defined the root stack inside of the layout.tsx, we now need to create a layout specifically for the tabs group. So, head over to tabs and create a new layout file layout.tsx. and let's create it together from scratch. We'll start by creating a new functional component called tab layout and make it equal to a react component. Then from within it, we can automatically return not a div. We don't have divs anymore. We're now working within web. But we'll return tabs. Tabs are going to be coming from expo router and we need to close them. If you used curly braces here, in that case you'll need to add a return statement here. But in this case, since we will be automatically returning it, there's no need to explicitly add a return. We can just use the parenthesis which mean auto return. So whenever you see this later on, you know the difference. So for these tabs, we can also apply a screen options of header shown is set to false. And within it, we are ready to display our screens by saying tabs.creen. You give it a name such as index. And you give it an options. So you can redefine its title. We don't want it to be just index. We want it to be home. And of course, don't forget to export default the tab layout. If you do that and reload the application, you'll now be able to see a bottom tab with all of these new pages. If you remove the header shown, you'll also be able to see it at the top. And now, because we use this option title home, it doesn't say index, which is the regular file name, but you can actually name it with a first capital letter. And I'll bring back the screen options to remove the header. Great. So now you can also see how home has a title at the bottom and inside settings and all of these just have their file name. So, let's recreate all of these different routes. We'll have 1 2 3 4 five I think in total. The second one is going to be called subscriptions. And the title will be the same word but with a capital letter. Then we'll have the insights. Same thing for the title. Finally, we'll have the settings and we'll have the settings right here. I was wrong. In total, we just need to have four and not five, which means I can remove the latest one. And now we have what appears to be a nicel looking bottom navigation. And even though we want to have just four, it seems like there's a fifth tab right here pointing to the subscription details page. This one belongs to this dynamic route, which I didn't want to have the tab for. So since we don't want to have a bottom tab navigation for it, we can just bring it back to the root route so that it exists and we can route to it but it is not part of the tab navigation. So now if you reload expose routing system autorecognizes it. So we can specifically set it to null by duplicating tabs.screen screen specify the route to subscriptions and then forward slash ID like this and then set the options of href to null like this which will then simply hide it from the bottom navigation bar. So what we've done here is that each tabscreen maps directly to one of the tabs within the tabs folder. Index points to index, subscriptions to subscriptions and so on. And the title is simply what shows up in the label as the label in the tab bar. So now you can already and successfully switch between those different pages. But now let's make it look better. Before we customize the top bar, we need to set up a couple of things. I'll provide you with the icons and the constants. You can grab these from the video kit link down in the description. That'll look something like this. And then at the bottom, you'll be able to see the codebase and the assets. So, click on the assets and then download this zipped assets folder from Google Drive. Once you download it, simply unzip it and then replace the existing assets folder by deleting it first and then simply drag and drop the new one right in. This folder will contain all of the icons and images we'll use throughout the rest of this application. Once you have the assets, let's create a file that uses them. I'll first create a folder called constants. And within constants, I'll create a new icons.ts file. You can find this file in the video kit link down in the description. Simply copy it and then paste it right here. You'll see what we're doing here is essentially we're just importing all of the assets and then exporting them so that we can more easily use them as icons. It seems to be complaining about the path. So, let's see why that is. It says that it cannot find it even though when I click on it, it works. This pattern will help us keep all of the icons centralized. So instead of importing them from the asset path, you can just import them from constants icons and get type safety throughout the icon type. But by default, TypeScript doesn't know how to handle image imports. So let's create a new file in the root of the application and call it image d.ts. D as in declaration. And I'll also provide it in the video kit link down in the description where we can tell it to understand how to read these image imports. So with that, this file will no longer complain. We'll also add another file at the root of the application called type.d.ts where we can declare all of the other types starting with the images. And you can pause the screen and copy it out, but basically we're telling it that we're working with images. And finally within the constants. So that's the same folder where we have the icons. Also create another file called data.ts. Within data we can import all of the icons coming from icons and then export different tabs which is going to be an array of tabs. Here we can create different objects containing a name such as index, title such as home and also we can provide an icon pointing to icons.home. You can duplicate this three times. The second one is going to be subscriptions. I mean you already know this because we've done something similar in the homepage which will replace with this icons is going to show to wallet. Then we have insights. The icon is going to show the activity. And finally, we'll have the settings with a title of settings pointing to icon. Setting. Now, before we go ahead and use these, we'll also want to modify the styles of our tab bar. And for that we'll need a tiny little package called clsx which you can install by running mpmi clsx which allows us to join different classes together. Then within the tab layout let's go ahead and update it. We no longer want to display these tabs one by one. What we want to do instead is just display the tabs like these and then map over the tabs coming from data.ts. And for each one of these, we want to return a tab.screen. So that's going to look like this. Tabscreen. Same as we've had before, but now we're doing it dynamically where we can pass a name equal to tab.name. We can also pass a key since we're mapping over it such as tab.name. We can pass the additional options. In this case, that's going to be the title coming from tab.title. And we can also pass the specific icon. So I'll put this into a new line so we can see it a bit better. And under options, you can provide a tab bar icon which is going to be different depending on whether it's focused or not focused or clicked or not clicked. So here we'll create a new little component within the tab layout component that I'll call tab icon. And it'll take in two different things. the focus state and the icon itself which are going to be of a type tab icon props. And now we can automatically return a view which in web world would be just a div. Don't forget to import this view. Now the reason this is red is because we used an immediate return but instead we want to use curly braces so that we can contain this within it and then explicitly return this part right here. Once you fix that, it'll no longer complain. And we can give this view a class name of tabs dash icon. Within it, we want to display those icons. So, I'll render another view with a class name set to dynamic property of clsx, which is the package we installed and which we have to import. And then it'll always render the tabs-pill class name which will make it rounded and properly positioned but only when it is focused. We also want to give it a class name of tabs-active and within it we want to display an image with a source pointing to the icon dynamic icon we want to display with a resize mode set to contain as well as a class name set to tabs dash glyph which is coming from CSS and basically it's going to apply a size of six to it and it looks like resize mode has been deprecated so I'll try not passing it in. Now we can use this tab icon right here under tab bar icon as a component and to it we can provide a focus state set to focused and the dynamic icon which is going to be set to tab icon. Add a comma here so we don't have any issues. And let's reload the application to be able to see the changes. It'll try to get access to the first asset that we have right here under constants coming from icons. But it says that it cannot find it by trying to import activity.png. Even though it seems to be here, I'll rerun the entire application, the server, by running mpx expo start. And I don't think we'll need clear this time. I just want to restart it because maybe these files didn't yet appear. And as you can see, I was kind of right because that allowed us to go past this error that we were initially seeing. So when developing React Native applications, trust yourself more. Maybe just the metro bundler is messing with you. So with that in mind, we can now see something interesting at the bottom. Not quite yet icons, but something is happening. Let's style it further because then it's going to look better. So under tabs where we have the screen options, we'll provide more properties within these screen options besides just header shown. I'll also add a tab bar show label and set it to false. We don't need to say home when we can show the home icon. I'll also modify the tab bar style by setting its position to be absolute. But even with this position absolute, it's not looking good. Here's the catch. On newer iPhones, there's a home indicator bar at the bottom. And on older devices, there isn't. So, if we hardcode a bottom value, the tab bar will either overlap the home indicator on new phones or float too high on the old ones. And that's where we want to use React Native safe area context. It's a super popular library with over 4 million weekly downloads, which basically handles those edge cases. So we can install it by typing this into the terminal. MPMI react native safe area context. And once you install it, you can just import use safe area insets coming from React Native safe area context. And then you can get the insets like this by simply calling it as a hook. This will tell you exactly how much space the devices hardware elements take up. The bottom inset on an iPhone 15 is around 34 pixels, but on an older iPhone with the home button, it's zero. And we can use that value to position the tab bar accordingly. So right here below position absolute, we can now use a math.max and choose from insets.bottom. And then we need to pass a horizontal insert right here. So create a new file under constants and call it theme. ts. This theme you can get from the but essentially it is nothing else than what we already have in the globals, just in the JavaScript format, so we can use it within our components. We have different colors right here, different spacings and different components or specifically the tab bar values for specific components. So once you have that you can now get access to it right here at the top by saying tab bar is equal to components tabbar and these components are coming from the theme. So you can import them as colors and components coming from add slash constants slash theme and then here under insets bottom we can also add those custom values tabbar horizontal inset. Now let's reload the app and you'll see that we have a little error right here under math.mmax. That's because here we're basically changing the bottom position. So that's what this math max has been changing all this time. So if you specify here that the bottom is for the mad do.mmax and reload you can see that now we moved it a bit above. Let's also add a height of the bar which is going to be tabbar.height. We can also add a margin horizontal which is going to be tabbar.h horizontal insert. We can add a border radius tabar. And this whole time we're trying to make the bar appear floating. Right? If it was just laying down at the bottom like it used to, that's fine. But the floating bar requires some work. Let's also change the background color to colors primary. There we go. Now you can see why we imported those colors so we can use them super simply like this. And then let's also change the border top width which is going to be set to zero as well as the elevation set to zero as well. So to center it, we can go outside of the tab bar style and modify the tab bar item style. And that's going to be padding vertical set to tab bar. / 2 minus the tab baricon frame divided by 1.6. I found this value to work the best, but we can play with it later on. And finally, we want to modify the tab bar icon style. We're going to set its width to tab bar icon frame, the height to tab bar icon frame, and align itself set to center. So now you can see that it is centered, but unfortunately we still can't see the actual icons. So let's see what that is all about. Under options, we're displaying the tab icon and passing the two different states. Then on top we have this tab icon where we're taking in the focus state and the icon itself. And we are returning a view that says tabs icon with a view class name tab spill and when focused also tabs active. And we're passing an image within it with a source of icon and a class name of tabs glyph. It's all looking good. But then why can't we see the image? Oh, it's coming from Expo image, but it should be coming from React Native itself because that's a base React Native component. So, if we fix this, you can see that now it works. And now that resize mode is not going to be deprecated anymore. We're working with a completely different component. So, take a look at that. You've just implemented this amazing floating bar navigation, and it's super fun to click around it. So, if you test the app a bit, you'll notice that the navigation is going to work, but the content on all the screens gets cut off behind the tab bar or the status bar at the top. So, let me show you how we can fix that. It's super simple. You just have to go to the tabs index.tsx and we have to wrap this part with a safe area view. So, instead of a regular view with these classes, use something known as a safe area view. coming from React Native safe area context, not from React Native. So right here, I'll say import safe area view from React Native safe area context. Let's give it a class name set to flex-1 bg background and a padding of five. And it won't accept the class names because we have to turn on the styled component. And to do that you have to import it under a different name like RN safe area view. And then you have to declare it like this. Safe area view is equal to styled which is coming from native wind RN safe area view. And this will allow you to pass the styling. And this styled part is coming from native wind. So you can just import it right here. There we go. Now you can see that the styling will get applied. The only reason why we have to do this is because the safe area view from the react native safe area context is a third party component and native wind needs the styled wrapper to enable class name support on it. And now you want to apply the same pattern to all three other pages. So I'll simply put this here. Copy these three lines and also add them to insights right here at the top. that's going to be safe area view and replace it here. So now if you add some additional class names and go to it, the insights will appear nicely within the screen. So let's copy it one more time. This time to the setting page. So also take it here. That's going to be settings and replace it with a safe area view with some class names. So the setting page gets fixed as well. And then finally, we have the last one, which is going to be the subscriptions page. So, we can paste the same imports right here. That's going to be these three lines. And then we can just use a safe area view instead of a regular view on the subscriptions page as well. So, take a look at that. We have our homepage with some navigation. the onboarding and the two odd screens haven't yet been set up properly, but all the pages within the tabs group and the tab layout itself works flawlessly and is already starting to feel like a real mobile app. So that's it. You now have a custom bottom tab navigator with themed icons, active states, and proper safe area handling for all the screens. Most importantly, you have a clean mental model for exactly how to build this tab navigation. We define our screens as files, then configure them through the data array and then import and map over them and define the styles directly within the tabs component. Next up, we'll build the onboarding screen. That's super useful, our first real screen. But before that, let's make sure our code is written well. So, let's push our code and we'll push it to a dev branch. A branch where we're going to be checking our code on before we merge it to main. Run git checkout dashb to create a new branch and switch to it called dev. Then run git add dot to add all the changes. And let's commit the changes by running something like git commit-m feat as in features we implemented project setup native wind routing and tab navigation. Press enter. We've done a lot already. And then run get push u origin dev. This is going to push the changes to the new branch. So now if you head back over to GitHub, you'll see that your repo and specifically your dev branch had recent pushes about 10 seconds ago. So compare them and open up a pull request and simply create it. By itself, we didn't really feel the need to add a large title or a description. But now, if somebody was reviewing this, there's a lot of files that they would need to review, some of which we implemented, most of which are actually just assets. So, it's good to have an AI tell the person reviewing, "Hey, here's what you can check out, like new features and some UI and style updates. You can check that out yourself, but instead let me provide you with a full AI generated review. So, let's give Code Rabbit about a minute to untangle our code like a pair of headphones. And the review is in. You get a quick walkthrough of everything we implemented together so far. A new tab-based navigation system introduced using Expo Router's tabs component with a reusable tab icon styling. We have constants that define the tab configuration, icon mappings, and design tokens. And we also use these four existing screens with safe area view and native wind styling. So let's go ahead and take a look at the changes. You get some nitpick comments which you can fix if you want to. Most often this is just about removing unused imports. And then let's take a look at the real review. Here it says that we have the incorrect route path that'll cause navigation failure. Specifically, it's referring to our dynamic route pointing to Spotify. Oh, that might be true because we changed its position, remember? So, if I go to Spotify, it really does break. So, this was a great catch by Code Rabbit. Then, you're given a couple of different ways to fix it. First, it's just a simple copy and paste fix. You can take this line of code, head back over to our tabs index.tsx, and just replace it right here with the right one. And immediately if we go back and revisit it again, it's going to work perfect. Other ways include an auto commit, which means that you can immediately just press commit changes and it'll push it to your codebase, which is great, and then prompt for an AI agent to fix in case more changes are needed across multiple files. But great, we got this one fixed. And we only have one other change needed right here, and that is to remove the unused view imports. Not a big deal for now, but it's always great to keep your code clean. So I'll remove it from all the files where we referenced it. It's going to be mostly within all of our screens. So that's going to be here, here, and here. And with that, we can go ahead and push those little fixes. So I'll type git add dot g get commit-m implement fixes and run g get push. Automatically github and code rabbit will recognize these new changes and we can just go ahead and merge this BR. Sure enough these fixes were super simple because the code so far is simple. But as we move into business logic and more complicated functionalities you'll see just how more detailed code rabbit will get. Great. Next up, typography. Default system fonts work fine for prototyping, but once you want your app to feel like a finished product, the font matters. It's one of the first things that your users notice subconsciously, even if they can't articulate why one app feels more polished over the other. For Recurly, we'll use Plus Jakarta Sans in multiple different weights, from light to bold. It's clean, modern, and it works well on mobile screens. I get most of my fonts directly from Google Fonts. You can just go here, search for the one you like, and then just download it. You can select different variants, and then download it, and you'll get a zip with TTF files. Now, on the web, using this font is super straightforward. You link a Google font or just add a font face rule, and you're done. But React Native doesn't work that way. The runtime doesn't automatically know about the files sitting in your assets folder. You need to explicitly load them before any UI renders. And that's what Expo font handles. It's already installed in your project since it comes with the default Expo template. We just need to use it. So there are two steps. First, register the fonts in your app config and then load them in your root layout before rendering anything. In this case, you don't have to download the fonts as I provided them to you right here under assets fonts. So, the only thing we have to do is head over into our app.json. And here we'll add an expo font plugin to the plugins array. So, search for plugins. And here you can see expo router. We have the expo splash screen below it. And then right after the splash screen, we'll also add the expo font, but it has to be in a new array. The first thing within the array is the actual plug-in that you want to add. And then as the second param, you can provide an object with additional options such as fonts. And then you can define this as an array where you can start listing out different fonts such as slash assets slash fonts slash plus jakarta sands regular.ttf and we'll actually implement a couple more like bold medium. We'll also do semibold. There's also an extra bold which we can use for a very heavy titles. And then we'll also need a light. Perfect. Make sure to add commas on all the lines besides the last one because we are in a JSON file. And that's it. This tells Expo which font files exist in the project. But registering them here isn't the same as loading them. They're not yet available for us to use. So step two is to load the fonts. So head back into our app layout.tsx. tsx this one right here. And we need to load the fonts before any screens render as we need to keep the splash screen visible while they load. So if we don't, users would see a flash of unstyled text before the fonts kick in. So that's why we're going to do it right here. So right at the top of the root layout, I'll say const and do array dstructuring fonts loaded and use the use fonts functionality coming directly from expo fonts. Make sure to import it right here. And then we can define all the different font families such as sans regular or different font types. It's going to be require such as dot dot slash assets slash fonts slash plus jakarta sans regular.ttf. And you want to fill it in for all of the different…

Transcript truncated. Watch the full video for the complete content.

Get daily recaps from
JavaScript Mastery

AI-powered summaries delivered to your inbox. Save hours every week while staying fully informed.