1. Courses
  2. Engineering Roles
  3. Mobile Apps with React Native
  4. Stop 1

35 minIntermediate

How React Native works

React Native lets you write a phone app in React and TypeScript, and the phone shows real Android and iOS screens. This lesson shows what happens between your component and the pixels, what Expo adds, how React Native compares with Flutter, fully native apps and web apps, and ends with the first screen of the Mangrove Rider app running on your own phone.

By the end you can

  • Explain what React Native renders, and what the JS thread and the UI thread each do
  • Describe the New Architecture (JSI, Fabric, TurboModules), Hermes and Metro in plain words
  • Choose between Expo Go and a development build, and know what prebuild, config plugins and EAS do
  • Compare React Native, Flutter, fully native and a PWA for a real product decision
  • Build a job card with core components, StyleSheet and Flexbox, and run it on a phone

The everyday picture

Think of how a new shelf layout reaches a store. A planner at head office decides the layout: rice on shelf two, milk in the second fridge, a festival display by the door. The planner never touches a shelf. Staff in the store read the plan and put real products on the store's own shelves and fridges. Every store has slightly different fixtures, so the same plan looks a little different in each one, but customers pick up real goods.

React Native works the same way. Your React code is the planner: it decides what the screen should contain. The phone's own UI system is the store staff: it creates real Android and iOS views. A web view inside an app is more like a poster of a shelf. It can look right, but it is a picture of a web page. And how the plan travels matters. Old React Native sent plans in batches by courier. The New Architecture gives the planner a direct phone line to the store.

What React Native really puts on the screen

When you write <View> and <Text> in React Native, no HTML is involved. Each of these becomes a real platform view: on Android a View becomes an android.view.ViewGroup, and on iOS a UIView. Scrolling, text input, the keyboard and accessibility come from the operating system, which is why a React Native app feels native and why TalkBack and VoiceOver can read it.

Two threads share the work. The JS thread runs your JavaScript: React renders your components, your event handlers run, your fetch calls start. The UI thread (also called the main thread) is the only one allowed to change native views, and it also receives touches. React Native's renderer turns one into the other in three steps, which the React Native docs call render, commit and mount:

  1. Render, usually on the JS thread: React runs your components and the renderer builds a matching tree of C++ objects, the .
  2. Commit, on a background thread: calculates the position and size of every node from your Flexbox styles.
  3. Mount, on the UI thread: the renderer compares the new tree with the one on screen and creates, updates or removes only the native views that changed.

This split explains most performance problems you will meet. If the JS thread is busy for 300 ms, for example parsing a large list of jobs, a tap still reaches the UI thread at once, but the code that answers the tap waits in line. The button looks frozen. On a cheap phone with 2 GB of RAM, slow JavaScript work is the usual cause of a "laggy" app. Fast on a cheap phone covers measuring and fixing it.

Quick check

What does the phone show when a React Native app renders <Text>Job 4521</Text>?

JS thread React code run by Hermes TurboModule camera, GPS JSI sync 1. render Fabric renderer (C++) 2. commit: Yoga layout 3. mount UI thread (main) Android ViewGroup iOS UIView 4. tap event
Your components run on the JS thread in Hermes. Through JSI they call native modules directly and hand the tree to the C++ renderer, Fabric. The renderer lays it out with Yoga and the UI thread mounts real native views. A tap travels back to your JavaScript as an event.

The New Architecture, Hermes and Metro in plain words

For years, JavaScript and native code talked through : every call was turned into a JSON message, queued, sent across, and decoded on the other side. Nothing could happen synchronously, and big messages were slow. The New Architecture replaces it with three parts:

  • lets JavaScript hold a reference to a C++ object and call its methods directly. No JSON, no queue, and a call can return a value at once.
  • is the renderer from the diagram above. Its core is C++ shared by both platforms, and it can measure and render synchronously when React needs it, which is what lets React's concurrent features, such as transitions, work on a phone.
  • are native modules (camera, location, secure storage) called through JSI. They load only when your code first uses them, and Codegen generates a typed contract between JavaScript and native code, which removes a common source of crashes.

The New Architecture has been the default since React Native 0.76, and since 0.82 it is the only architecture: the opt-out flags are ignored. Any library you add must support it. Check its documentation or the React Native Directory before you install it.

is the JavaScript engine that runs your code, and it is the default. In a release build, your JavaScript is compiled to Hermes bytecode at build time, so the phone does not parse source code at startup. The React Native docs report better startup time, lower memory use and a smaller app compared with the older engine, which matters on a 2 GB Android phone.

is the bundler. During development it runs on your laptop, combines your TypeScript files into one JavaScript bundle and serves it to the phone over your Wi-Fi. When you save a file, Fast Refresh swaps the changed component and keeps its state. For a release, the bundle is built once and shipped inside the app.

Quick check

On the New Architecture, the Rider app's code asks a native location module for the last known position. What is different from the old bridge?

Expo: the framework around React Native

React Native gives you the renderer and the core components. A real app also needs a camera, location, notifications, secure storage, icons, splash screens, signed builds and updates. is the framework that supplies them, and the React Native docs recommend starting new apps with a framework like it. Each Expo SDK release is tested with one React Native version. At the time of writing (October 2026), the newest is Expo SDK 58 with React Native 0.88 and React 19.3.

You run your code in one of two "shells", and choosing the right one saves days:

  • is a ready-made app from Expo. It already contains the native code of the Expo SDK modules, so you scan a QR code and your JavaScript runs in it in seconds. Expo calls it an educational tool for first steps: you cannot add your own native code, and each Expo Go build runs one SDK version.
  • A is your own app, compiled with the expo-dev-client library. It contains exactly the native libraries you chose, has your app's name and icon, and still loads JavaScript from Metro with Fast Refresh. Real apps use one from the first week.

Where does the native project come from? With , you do not keep the android and ios folders. You describe the app in app.json (name, icons, permissions), and npx expo prebuild generates both folders from a template. A is a function that edits the generated project during prebuild; libraries ship them, so installing a camera library also adds its Android permission and its iOS usage text. New projects list both folders in .gitignore, and upgrading the SDK means regenerating them.

is Expo's cloud service. EAS Build compiles your app on Expo's servers, so you can make an iPhone build without a Mac. EAS Submit uploads it to the stores, and EAS Update sends JavaScript fixes to installed apps. Testing and releasing covers all three.

Quick check

You want to try a new card layout on your phone in the next ten minutes. It uses only View, Text and Pressable. What is fastest?

Your laptop TSX code Metro bundler bundles JS bundle over Wi-Fi Your phone Expo Go fixed native code SDK modules only for trying ideas Dev build your native code expo-dev-client for the real app EAS Build prebuild, plugins Play / App Store new native library? needs a dev build
Metro bundles your code and serves it to the phone. Expo Go runs it on top of fixed native code, which is fine for trying ideas. A development build carries your own native libraries; EAS Build makes it from app.json and config plugins, and the same setup becomes your store build.

React Native, Flutter, fully native or a PWA?

Four options can put the Rider app on a phone. Each one wins somewhere, and a team should choose for reasons it can say out loud.

OptionWho draws the UIWins whenCosts
React Native (Expo)The platform's own viewsYour team knows React and TypeScript, you want iOS and Android from one codebase, and you want native look, gestures and accessibilityA bridge to native code for anything Expo and libraries do not cover; two platforms to test
FlutterFlutter's own engine paints every pixelYou want the same custom design on every device and the team writes DartA separate language and widget set from your web stack; native look is imitated, not inherited
Fully native (Kotlin, Swift)The platform, directlyDeep OS work (widgets, watch apps, heavy camera or audio processing), day-one access to new OS features, or the tightest performanceTwo codebases, two teams or two skill sets, every feature built twice
PWA (web app)The browserUsers open it once from a link, no install, instant updates, one web teamLimited background work and device access; on iOS, web push needs the user to add the app to the Home Screen first (iOS 16.4 and later)

Three questions settle most decisions. Who will build and maintain it, and what do they already know? What device features must work, and do they need to run in the background? How do people arrive: do they install an app they use every day, or tap a link once? A delivery app like Uber or Grab, which riders use all shift, needs background location, reliable notifications and offline storage, so it is an installed app. The page a customer opens from a "your order is on the way" text message is a web page.

Core components, styles and Flexbox on a phone

Six components build most screens. View is the box, like a div. Text holds every piece of text; a string directly inside a View is an error. Image shows local or remote images, and a remote image needs a width and height. Pressable makes anything tappable and tells you when it is pressed. ScrollView scrolls its content; it renders every child at once, so it suits a short screen, and long lists use FlatList instead (see Fast on a cheap phone). TextInput is the text field, with props such as keyboardType="number-pad" for a one-time code.

Styles are JavaScript objects with camelCase names, usually grouped with StyleSheet.create. You pass them in the style prop, and an array merges them, with the last one winning: style={[styles.status, isLate && styles.late]}. Four web habits break:

  • No cascade. There are no selectors or class names. A colour set on a View does not reach the Text inside it. Text styles are inherited only from a parent Text. To share a font, you make your own component, for example AppText.
  • Different Flexbox defaults. Every View is a flex container. flexDirection defaults to column, not row; flexShrink defaults to 0, not 1; alignContent defaults to flex-start; and flex takes one number, such as flex: 1. A long store name next to a status badge pushes the badge off the screen until you give the name flexShrink: 1.
  • Numbers, not units. padding: 16 means 16 . There is no px, rem or vh. Percent strings such as width: '50%' work.
  • No grid or float. Layout is Flexbox, plus position: 'absolute' for overlays.

Platforms differ in small ways you must handle. Platform.OS is 'android' or 'ios', and Platform.select picks a value per platform, for example a shadow on iOS and elevation on Android. Phones also have notches, rounded corners and gesture bars, so content must stay inside the . Use SafeAreaView or useSafeAreaInsets from react-native-safe-area-context, which Expo's default template already includes. The SafeAreaView built into React Native is deprecated.

Quick check

A job card shows the store name and a "Late" badge in a row. With "Mangrove Market, Riverside Road North", the badge disappears off the right edge. What fixes it?

Build it

Create an Expo app, run it on your own phone, and build the Rider app's first screen: a list of job cards. You need Node.js (an LTS version) and a phone on the same Wi-Fi as your laptop. Commands work in Terminal on macOS and in PowerShell or Git Bash on Windows.

  1. Create the project and start Metro

    Terminal
    npx create-expo-app@latest rider-app
    cd rider-app
    npx expo start

    The default template uses TypeScript and Expo Router, with screens in an app folder (recent templates put it under src). npx expo start starts Metro and prints a QR code. Versions move fast here: at the time of writing, during the move to SDK 58, the plain command creates an SDK 57 project, which Expo's docs recommend if you will use Expo Go on a phone. Adding --template default@sdk-58 creates an SDK 58 project.

  2. Open it on a real phone

    The easiest phone for this is an Android one, which is also what most riders carry. Install Expo Go from the Google Play Store, then scan the QR code from inside Expo Go. iPhones are harder in 2026: the App Store copy of Expo Go stops at SDK 54, so for newer SDKs Expo's setup guide builds your own copy with npx eas-cli@latest go and installs it through TestFlight, which needs a paid Apple Developer Program membership. On a Mac, the iOS Simulator is a free alternative. On iOS, sign in to the same Expo account in Expo Go and with npx expo login. If the phone cannot connect, for example on office or public Wi-Fi, restart with a tunnel, which is slower but works across networks:

    Terminal
    npx expo start --tunnel

    Change the title text in the home screen file and save. The phone updates in about a second. That is Fast Refresh.

  3. Add the job type and sample data

    Create a features/jobs folder next to the app folder, with the job type from the Rider API. Money is integer cents, so $12.50 never turns into 12.499999.

    TypeScript
    // features/jobs/types.ts
    export type JobStatus = 'assigned' | 'accepted' | 'picked-up' | 'delivered' | 'failed';
    
    export type Job = {
      id: string;
      orderId: string;
      storeName: string;
      dropAddress: string;
      dropLat: number;
      dropLng: number;
      status: JobStatus;
      codCents: number;                      // 0 means prepaid
      slot: { start: string; end: string };  // ISO times, for example a 30-minute "Now" slot
    };
    
    export type JobCardView = {
      title: string; statusLabel: string; codLabel: string; late: boolean;
    };

    Add features/jobs/sampleJobs.ts exporting sampleJobs: Job[] with three jobs: one prepaid, one cash on delivery of 1250 cents, and one whose slot ended an hour ago.

  4. Write the logic as a plain function

    Keep what the card shows out of the component, so you can test it without a phone. Solve the first Practice task below, then put your function in features/jobs/jobCardView.ts with types: export function jobCardView(job: Job, nowIso: string): JobCardView.

  5. Build the card with core components

    TypeScript
    // features/jobs/JobCard.tsx
    import { Platform, Pressable, StyleSheet, Text, View } from 'react-native';
    import type { Job } from './types';
    import { jobCardView } from './jobCardView';
    
    type Props = { job: Job; now: string; onPress: (id: string) => void };
    
    export function JobCard({ job, now, onPress }: Props) {
      const view = jobCardView(job, now);
      return (
        <Pressable
          accessibilityRole="button"
          onPress={() => onPress(job.id)}
          style={({ pressed }) => [styles.card, pressed && styles.pressed]}
        >
          <View style={styles.row}>
            <Text style={styles.store} numberOfLines={1}>{view.title}</Text>
            <Text style={[styles.status, view.late && styles.late]}>
              {view.late ? 'Late' : view.statusLabel}
            </Text>
          </View>
          <Text style={styles.address} numberOfLines={2}>{job.dropAddress}</Text>
          <Text style={styles.cod}>{view.codLabel}</Text>
        </Pressable>
      );
    }
    TypeScript
    // the end of JobCard.tsx
    const styles = StyleSheet.create({
      card: {
        backgroundColor: '#ffffff', borderRadius: 12, padding: 16, marginBottom: 12,
        ...Platform.select({
          ios: { shadowColor: '#000000', shadowOpacity: 0.1, shadowRadius: 6, shadowOffset: { width: 0, height: 2 } },
          android: { elevation: 2 },
        }),
      },
      pressed: { opacity: 0.7 },
      row: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' },
      store: { flexShrink: 1, fontSize: 17, fontWeight: '600' },  // shrink, or the badge is pushed off
      status: { marginLeft: 8, fontSize: 14, color: '#2f6f4f' },
      late: { color: '#b3261e', fontWeight: '700' },
      address: { marginTop: 4, fontSize: 15, color: '#444444' },
      cod: { marginTop: 8, fontSize: 16, fontWeight: '600' },
    });

    Note the row: flexDirection: 'row' is needed because the default is a column. Each Text sets its own font, because nothing cascades from the View.

  6. Show the list on the home screen

    Replace the home route (the index.tsx in the app folder). If your home route sits one folder deeper, add one more ../ to the imports.

    TypeScript
    import { ScrollView, StyleSheet, Text } from 'react-native';
    import { SafeAreaView } from 'react-native-safe-area-context';
    import { JobCard } from '../features/jobs/JobCard';
    import { sampleJobs } from '../features/jobs/sampleJobs';
    
    export default function HomeScreen() {
      const now = new Date().toISOString();
      return (
        <SafeAreaView style={styles.screen} edges={['top']}>
          <Text style={styles.heading}>Jobs for today</Text>
          <ScrollView contentContainerStyle={styles.list}>
            {sampleJobs.map((job) => (
              <JobCard key={job.id} job={job} now={now} onPress={(id) => console.log('open job', id)} />
            ))}
          </ScrollView>
        </SafeAreaView>
      );
    }
    
    const styles = StyleSheet.create({
      screen: { flex: 1, backgroundColor: '#f2f4f3' },
      heading: { fontSize: 22, fontWeight: '700', paddingHorizontal: 16, paddingVertical: 12 },
      list: { paddingHorizontal: 16, paddingBottom: 24 },
    });

    On the phone, check three things: the heading clears the notch or status bar, the late job shows a red "Late", and a long store name ends with "…" instead of pushing the badge away. Tap a card and the "open job" line appears in the terminal where Metro runs. Navigation and app structure turns that tap into a job detail screen.

  7. Know your next step: a development build

    Expo Go is enough for this screen. The day you add a native library Expo Go does not contain, make a development build instead. These commands install the dev client, log in to EAS, create eas.json with a development profile, and build an Android app you install on your phone:

    Terminal
    npx expo install expo-dev-client
    npm install -g eas-cli
    eas login
    eas build:configure
    eas build --profile development --platform android

    After you install it, npx expo start opens your code in your development build instead of Expo Go. Install native libraries with npx expo install, which picks the version that matches your SDK.

Bring it to your team

Questions to ask when a team starts a React Native app or adds to one.

  • Which Expo SDK and React Native version are we on, and who owns upgrading once or twice a year?
  • Do all our native libraries support the New Architecture? Where did we check?
  • Are we testing on a cheap Android phone with 2 or 3 GB of RAM, or only on our own phones?
  • Are the android and ios folders generated by prebuild, and are our native changes in config plugins instead of hand edits?
  • Is logic such as status labels, money formatting and late flags in plain functions with tests, outside the components?

Red flags

  • "It works in Expo Go" used as proof that a feature that needs a native library is done.
  • Hand edits in a generated android folder that the next prebuild will wipe out.
  • Money stored as dollars in floating point, such as 12.5, instead of integer cents.
  • A library installed with plain npm install at a version that does not match the SDK.
  • Choosing a platform because "everyone uses it", with no answer on team skills, device features and how users arrive.

Practice

What the job card shows

Write jobCardView(job, nowIso). It returns { title, statusLabel, codLabel, late }. title is the store name. statusLabel maps the status: assigned to "New job", accepted to "Accepted", picked-up to "On the way", delivered to "Delivered", failed to "Failed", anything else to "Unknown". codLabel is "Collect $12.50" for 1250 cents and "Prepaid" for 0. late is true only when the time now is after slot.end and the job is not yet delivered or failed.

JavaScript exerciseRuns in your browser with tests
function jobCardView(job, nowIso) {
  // job: { id, storeName, dropAddress, status, codCents, slot: { start, end } }
  // times are ISO strings such as '2026-10-20T18:30:00Z'
  return {
    title: job.storeName,
    statusLabel: job.status,
    codLabel: 'Collect ' + job.codCents / 100,
    late: false,
  };
}
Ctrl + Enter runs
Show a solution
JavaScript
const STATUS_LABELS = {
  assigned: 'New job', accepted: 'Accepted', 'picked-up': 'On the way',
  delivered: 'Delivered', failed: 'Failed',
};

function formatCents(cents) {
  const dollars = Math.floor(cents / 100);
  const rest = String(cents % 100).padStart(2, '0'); // 905 -> "05"
  return '$' + dollars + '.' + rest;
}

function jobCardView(job, nowIso) {
  const open = job.status !== 'delivered' && job.status !== 'failed';
  return {
    title: job.storeName,
    statusLabel: STATUS_LABELS[job.status] ?? 'Unknown',
    codLabel: job.codCents > 0 ? 'Collect ' + formatCents(job.codCents) : 'Prepaid',
    late: open && Date.parse(nowIso) > Date.parse(job.slot.end),
  };
}
Pass the exercise to finish this task.

Port a web style and flag what will not work

The back office's job card has web CSS. Write webToNativeStyle(css): it takes an object of CSS properties in kebab-case and returns { style, unsupported }. Convert names to camelCase. Turn values like "12px" into the number 12, and plain number strings like "1" into numbers. Keep other values, such as colours and percentages, as strings. Do not copy, and list in unsupported (in input order), any property that is float or grid-template-columns, any display other than flex or none, any value ending in em, rem, vh or vw, and any flex value that is not a single number.

JavaScript exerciseRuns in your browser with tests
function webToNativeStyle(css) {
  const style = {};
  const unsupported = [];
  for (const [name, value] of Object.entries(css)) {
    const key = name.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
    style[key] = value; // TODO: numbers, and flag what React Native cannot do
  }
  return { style, unsupported };
}
Ctrl + Enter runs
Show a solution
JavaScript
const NOT_IN_RN = new Set(['float', 'grid-template-columns']);
const NUMBER = /^-?\d+(\.\d+)?$/;
const PX = /^-?\d+(\.\d+)?px$/;

function webToNativeStyle(css) {
  const style = {};
  const unsupported = [];
  for (const [name, raw] of Object.entries(css)) {
    const value = String(raw).trim();
    const bad =
      NOT_IN_RN.has(name) ||
      (name === 'display' && value !== 'flex' && value !== 'none') ||
      /(em|vh|vw)$/.test(value) ||               // also catches rem
      (name === 'flex' && !NUMBER.test(value));   // flex: 1, never '1 1 auto'
    if (bad) { unsupported.push(name); continue; }
    const key = name.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
    if (PX.test(value)) style[key] = parseFloat(value);
    else if (NUMBER.test(value)) style[key] = Number(value);
    else style[key] = value;
  }
  return { style, unsupported };
}
Pass the exercise to finish this task.

Check yourself

On a cheap Android phone, riders say the "Accept" button takes almost a second to react while the job list refreshes. The refresh parses a large JSON response and re-renders 200 cards. What is the most likely cause?

The team wants a third-party barcode library with native Android and iOS code for the pickup scan. What should change in how they run the Rider app?

Mangrove wants customers to see where their rider is after a "your order is on the way" text message. Most customers will look once or twice per order. What is the best fit?

Flashcards

Open my review

Key takeaways

  • React Native renders real native views. Your code runs on the JS thread in Hermes; only the UI thread changes views, so a busy JS thread makes taps feel slow.
  • The New Architecture (default since 0.76, the only one since 0.82) uses JSI for direct calls, Fabric to render and lazily loaded TurboModules for native features.
  • Expo is the framework: SDK modules, Expo Go for quick tries, development builds for real apps, prebuild and config plugins instead of hand-edited native folders, and EAS for builds and updates.
  • Choose React Native, Flutter, fully native or a PWA by team skills, device features and how people arrive, not by fashion.
  • Styles are objects without a cascade, Flexbox defaults to a column with no shrinking, sizes are unitless, and content stays inside the safe area.

Sources

Episode transcriptPlanner, shelves and a direct line · about 12 minutes · tap any line to play from there
  1. NadiaImran, picture a rider in a basement car park. She taps Take photo for a proof of delivery, the camera opens, and when she comes back, the form she half filled in is gone. What happened?
  2. ImranI know this one, because it really happened to the first Rider app. It was a web page, and the browser reloaded it while the camera was open.
  3. NadiaRight. On a phone with two or three gigabytes of memory, the browser threw the page away. Today is about what React Native does differently, what Expo adds on top, and how you get the first Rider screen onto a real phone.
  4. NadiaStart with a store. A planner at head office decides the shelf layout. Rice on shelf two, milk in the second fridge. The planner never touches a shelf. Store staff read the plan and put real goods on the store's own shelves.
  5. ImranSo my React code is the planner. It decides what the screen should contain.
  6. NadiaYes. And the phone's own UI system is the store staff. It builds real Android and iOS views. A web view inside an app is more like a poster of a shelf. It can look right, but nobody can pick anything off it.
  7. ImranAnd every store has slightly different fixtures, so the same plan looks a bit different on Android and on an iPhone.
  8. NadiaExactly. So when you write a View in React Native, Android gets a real view group and iOS gets a real UI view. Scrolling, the keyboard and the screen reader come from the operating system.
  9. ImranThen where does my JavaScript run? Inside some hidden browser?
  10. NadiaNo browser at all. It runs in Hermes, a JavaScript engine built for React Native, on its own thread, the JS thread. A second thread, the UI thread, is the only one allowed to touch native views. It also receives every touch.
  11. NadiaIf you can see your screen, the first diagram goes top to bottom. Your React code on the JS thread at the top, the C++ renderer called Fabric in the middle, and the UI thread with real Android and iOS views at the bottom.
  12. ImranAnd the steps between them?
  13. NadiaThree. Render: React runs your components and the renderer builds a tree of C++ objects, the shadow tree. Commit: a layout engine called Yoga works out every position and size from your Flexbox styles. Mount: the UI thread changes only the native views that differ.
  14. ImranSo if a tap feels slow on a cheap phone, my guess is the UI thread is overloaded drawing things.
  15. NadiaThat's the tempting guess, and it's usually wrong. The UI thread gets the tap straight away. But your on press handler runs on the JS thread. If that thread is busy parsing a big list of jobs, your handler waits in line. The button looks frozen.
  16. ImranSo slow JavaScript is what makes the app feel laggy. That's the lesson on fast apps for cheap phones, later in the course.
  17. NadiaYes. Now the phone line from the analogy. Old React Native had the bridge. Every call between JavaScript and native code became a JSON message, queued and decoded on the other side. Nothing could happen synchronously.
  18. ImranLike sending the shelf plan by courier, in batches.
  19. NadiaGood picture. The New Architecture replaced the courier with a direct line, called JSI, the JavaScript Interface. JavaScript can hold a reference to a native object and call it directly, and a call can return a value straight away.
  20. ImranAnd the other names I keep hearing? Fabric and TurboModules?
  21. NadiaFabric is the renderer we just walked through, written in C++ and shared by both platforms. TurboModules are native modules, like the camera or location, reached through JSI. They load only when your code first uses them, which helps startup.
  22. ImranIs this new architecture optional?
  23. NadiaNot any more. It became the default in React Native zero point seventy-six, and since zero point eighty-two it's the only architecture. So every library you add must support it. Check before you install.
  24. ImranAnd Metro? It's always printing things in my terminal.
  25. NadiaMetro is the bundler. During development it runs on your laptop, turns your TypeScript files into one JavaScript bundle and serves it to the phone over Wi-Fi. Save a file and Fast Refresh swaps the changed component in about a second.
  26. ImranNow Expo. Some of my developers say Expo is only for beginners.
  27. NadiaThat was true years ago. Today Expo is the framework around React Native. It gives you the camera, location, notifications and secure storage as SDK modules, plus file-based routing, cloud builds and updates. The React Native docs recommend starting with a framework like it.
  28. ImranAnd Expo Go is the app where you scan a code and your project appears.
  29. NadiaYes, and Expo calls it an educational tool. It has fixed native code inside. You can't add your own. The moment you need a native library it doesn't contain, you make a development build, which is your own app with your own native code that still loads JavaScript from Metro.
  30. NadiaThe second diagram shows both. Metro on your laptop at the top, sending the bundle over Wi-Fi to the phone. On the left, Expo Go, for trying ideas. On the right, your development build, which EAS Build turns into the store build at the bottom.
  31. ImranEAS being Expo's cloud service.
  32. NadiaRight. EAS Build compiles the app on Expo's servers, so you can make an iPhone build without a Mac. EAS Submit sends it to the stores, and EAS Update sends JavaScript fixes. The last lesson of the course covers releasing.
  33. ImranAnd where do the Android and iOS project folders live? I've seen teams with huge native folders in the repo.
  34. NadiaWith Expo you usually don't keep them. You describe the app in the app config, and prebuild generates both folders. A config plugin is a small function that edits them during prebuild, for example to add a camera permission. Hand edits there get wiped out next time.
  35. ImranNow the question my boss always asks. Why React Native and not Flutter, or real native, or a web app?
  36. NadiaEach one wins somewhere. Flutter paints every pixel with its own engine, so the design is identical everywhere, and you write Dart. Fully native, Kotlin and Swift, wins for deep operating system work, but you build every feature twice.
  37. NadiaA web app wins when people arrive from a link, use it once and leave. No install, instant updates. But background work and device access are limited, and on an iPhone, web push only works after the user adds the app to the Home Screen.
  38. ImranSo three questions. Who builds it and what do they know? Which device features must work, maybe in the background? And do people install it and use it every day, or tap a link once?
  39. NadiaThat's it. And it's exactly the note I wrote for Ken when we replaced that web page. The Mangrove app team already writes React and TypeScript, so they can share the API client and the job types. Camera, location and notifications come from Expo. Nobody writes Kotlin or Swift for version one.
  40. ImranAnd the web page didn't disappear completely, did it?
  41. NadiaNo. The customer's track your rider page stayed on the web, because people open it once from a text message. The one risk in the note: any future native library, like a card reader for cash on delivery, must support the New Architecture.
  42. ImranNow styling. My web developers will try to paste CSS. What breaks?
  43. NadiaFour things. No cascade: a colour on a View does not reach the Text inside it. Flexbox defaults differ: direction is column, and flex shrink is zero. Sizes are plain numbers, density-independent pixels, with no px, rem or vh. And there's no grid or float.
  44. ImranFlex shrink zero. So a long store name next to a Late badge pushes the badge off the screen.
  45. NadiaExactly the bug in the lesson's checkpoint. Give the name flex shrink one and it wraps or gets cut with dots. And keep content inside the safe area, away from notches and gesture bars, with the safe area context library Expo already includes.
  46. NadiaThe Build-it puts this on a phone. You create the app with create expo app, start Metro, and open it in Expo Go. An Android phone is easiest. On an iPhone in twenty twenty-six you need your own copy of Expo Go through TestFlight, or a Mac with the simulator.
  47. ImranThen I add the job type, with money in integer cents, write the card logic as a plain function, and build a job card from Pressable, View and Text.
  48. NadiaThen a home screen with a scroll view of sample jobs inside a safe area view. You check that the late job shows a red Late, and that a long store name doesn't push the badge away. The last step shows the commands for a development build with EAS.
  49. ImranAnd the practice section has the logic as exercises in the browser. Status labels, cash on delivery in dollars and cents, and the late flag. Plus a converter that flags web styles React Native can't use.
  50. ImranFor my team. Which Expo SDK are we on, and who owns upgrading? Do all our native libraries support the New Architecture? And are we testing on a cheap Android phone, or only on our own expensive ones?
  51. NadiaAdd one more. Are our native changes in config plugins, or did someone hand edit a generated folder that the next prebuild will wipe out?
  52. NadiaOne to think about. The team wants a barcode library with its own native code for the pickup scan. They install it, open the app in Expo Go, and it doesn't work. Why? Take a second.
  53. ImranBecause Expo Go only has its own built-in native code. The library's native part isn't in there. They need a development build, and the library's config plugin sets it up during prebuild.
  54. NadiaExactly right.
  55. ImranMy summary. One: React Native shows real native views. JavaScript runs on its own thread in Hermes, and only the UI thread touches views, so busy JavaScript makes taps feel slow.
  56. ImranTwo: the New Architecture means JSI for direct calls, Fabric for rendering, and TurboModules for native features. It's the only architecture now.
  57. ImranThree: Expo Go is for trying things. Real apps use a development build, with prebuild and config plugins instead of hand-edited native folders.
  58. NadiaOne nuance on the first point. A slow tap is rarely the UI thread's fault. Look at what your JavaScript is doing at that moment first.
  59. ImranLike the planner who's too busy to answer the phone.
  60. NadiaExactly. Next time: navigation. Stacks, tabs, and a link that opens one job straight from a notification.
Finished reading?Mark it complete to fill in its stop on your course line.

Use it at work

Loyalty card screen (React)

A phone screen with its points rules in a tested helper file.

Open in the Code Lab
Next stopNavigation and app structure
Was this lesson helpful?
Questions and discussion

Loading the discussion…