React Native Prototype: A Small, Verifiable Start
Mobile prototypes only matter if they run on an actual simulator or device. A repo that “looks right” but will not boot is just text.
This post is a small React Native prototype you can verify. The goal is not to build an app. The goal is to create a fresh project, change one file, and prove you can see your change on screen. Once you can do that, everything else becomes normal incremental work instead of guesswork.
What you are building
You are building the smallest possible React Native app that prints a single line of text in a safe area. That sounds boring, but it is the right baseline because it proves the entire chain works: Node can run the CLI, the native project exists, the bundler can serve JavaScript, the native shell can load it, and the screen can render.
React Native is just React code running inside a native shell (Source). The “native” part is real. iOS builds use Xcode (Source). Android builds use Gradle and the Android toolchain (Source). The JavaScript is still yours, but the last mile is a real native app.
What you need and why you need it
React Native is one of those setups where missing one piece can waste a whole night. So here is what matters and why.
You need Node because the React Native CLI is a Node tool (Source). You need Xcode if you plan to run iOS because iOS builds are Xcode projects under the hood (Source). You need Android Studio if you plan to run Android because it provides the SDK, emulator, and build tools that Gradle expects (Source).
Here is a quick summary of what each prerequisite gives you:
- Node.js — runs the React Native CLI and Metro bundler; required for every platform (Source)
- Xcode (macOS only) — provides the iOS SDK, Simulator, and build system for
run-ios(Source) - Android Studio — provides the Android SDK, emulator images, and Gradle build tools for
run-android(Source) - CocoaPods (iOS only) — manages iOS native dependencies during the pod install step (Source)
- JDK 17+ — required by Gradle for Android builds; bundled with recent Android Studio versions (Source)
If you only want one platform right now, pick one. iOS on macOS is usually the smoother path because Xcode + Simulator are tightly integrated. Android is totally fine too, but your setup surface area is bigger because the SDK, emulator image, and Java requirements have more moving parts.
iOS vs Android setup comparison
| Aspect | iOS (macOS) | Android (any OS) |
|---|---|---|
| Primary tool | Xcode + Simulator | Android Studio + Emulator |
| Build system | Xcode projects / CocoaPods | Gradle + Android SDK |
| Setup surface | Smaller — tightly integrated | Larger — SDK, emulator image, JDK |
| Common failure | Xcode license not accepted | SDK path or Java version mismatch |
| First-run speed | Usually faster on macOS | More configuration steps |
Expo vs React Native CLI
This post uses the React Native CLI path. Here is how it compares to Expo:
| Aspect | React Native CLI | Expo |
|---|---|---|
| Native folders | ios/ and android/ generated | Hidden / managed by Expo |
| Native code access | Full control | Limited unless you eject |
| Setup complexity | Higher — you manage the toolchain | Lower — Expo handles native parts |
| Best for | Learning the native toolchain | Fast prototyping without native code |
Source: React Native Environment Setup
Quick environment sanity checks
Before you scaffold anything, do these checks. They are not “optional best practices.” They are just the fastest way to confirm you are not walking into a setup trap.
- Check Node — confirm Node is installed and not an ancient version (Source)
- Check Xcode (iOS only) — confirm command line tools are installed and the license is accepted (Source)
- Check Android Studio (Android only) — confirm the SDK is installed and at least one emulator image exists (Source)
First, confirm Node exists and is not ancient.
node -vnpm -v
If you do not see versions printed, stop and fix Node first. A lot of React Native errors are just “you are missing Node” wearing a mask.
Second, if you are doing iOS, confirm Xcode command line tools are installed and the license is accepted. This is one of the most common iOS failure modes.
xcode-select -psudo xcodebuild -license accept
Third, if you are doing Android, confirm you have Android Studio installed and that you can open the SDK Manager and install at least one emulator image. If you do not have an emulator that can boot, run-android has nothing to target.
How to build it
Step 1: Create the project
This is the scaffold step. It creates the repo, the native shells, and the default starter app.
npx react-native@latest init NativeLabcd NativeLab
What that command actually does matters. It generates a project folder, installs the JavaScript dependencies, and creates the native folders (ios/ and android/) that Xcode and Gradle will build (Source). If those folders do not exist, you do not have a real React Native project.
Verify it worked:
Open the folder and confirm you see ios/ and android/.
If it fails, do not try to “fix it by guessing.” Read the exact CLI output. Most failures are missing prerequisites or permissions.
Step 2: Edit the one file that proves everything
React Native projects can get noisy fast. The point here is to keep the first change dead simple so you can see the feedback loop.
Open App.tsx and replace it with this:
import React from "react";import { SafeAreaView, Text } from "react-native";export default function App() {return (<SafeAreaView style={{ padding: 24 }}><Text>NativeLab is running.</Text></SafeAreaView>);}
Here is what that code is doing in plain terms. SafeAreaView prevents your content from sitting under the notch or the status bar on iOS (Source). The inline padding: 24 makes it obvious the layout is yours, not the default template. The Text component is the simplest visible output you can render on every platform (Source).
Verify it worked:
The file should save cleanly with no TypeScript errors. If your editor highlights the imports as missing, it usually means your dependency install did not complete.
Step 3: Run it on iOS
If you are on macOS and you installed Xcode, this is the most direct proof.
npx react-native run-ios
What is happening during this step is important. The CLI kicks off a native build in the background, launches Simulator, starts Metro (the JavaScript bundler) (Source), then the app connects to Metro to load your JavaScript bundle (Source).
Verify it worked:
You should see the Simulator open and the text NativeLab is running. on screen.
If it fails, the most common causes are: Xcode not installed, Xcode license not accepted, or missing command line tools.
Step 4: Run it on Android
Android requires an emulator or device connected through ADB (Source). Start the emulator first, then run:
npx react-native run-android
Android failures are often not “React Native problems.” They are toolchain problems. Wrong SDK path, no emulator running, Gradle failing, Java version mismatch (Source). The good news is that once you get one project to run, the next ones are much easier because the machine is configured.
Here are the most common Android failure categories:
- Wrong SDK path —
ANDROID_HOMEis not set or points to the wrong folder - No emulator running —
run-androidhas nothing to target - Gradle failure — usually a Java version mismatch or missing dependency
- Java version mismatch — Gradle requires a specific JDK version (Source)
Verify it worked:
The emulator should open the app and show the same text.
What “running” actually means
A lot of beginners think “it ran” means “the command finished.” In React Native, “it ran” means you can change JavaScript and reliably see the change on screen. That is the real prototype loop.
So after you see the screen once, change the text to something else, save, and confirm the app updates. That is your proof that Metro is serving your bundle correctly and the app is connected.
Common failures that look like React Native but are not
React Native errors are usually symptoms, not causes. Here are the ones that show up the most.
Quick failure reference table
| Symptom | Likely cause | Fix |
|---|---|---|
| Blank screen / bundle loading | Metro not running | npx react-native start |
| iOS build fails immediately | Xcode missing or license not accepted | xcode-select -p + sudo xcodebuild -license accept |
| Android cannot find device | No emulator running or ADB not connected | Start emulator, run adb devices |
| Gradle errors | SDK path, Java version, or dependency issue | Read first error line, check ANDROID_HOME and JDK |
Metro is not running
If the app launches but you see a blank screen or bundle loading issues, Metro might not be running.
Run:
npx react-native start
Keep that terminal open, then in a second terminal run run-ios or run-android again.
iOS build fails immediately
If you see build failures that look like Xcode toolchain errors, it usually means Xcode is missing or not configured.
The two fastest checks:
xcode-select -psudo xcodebuild -license accept
Then retry the run command.
Android says it cannot find a device
That is almost always “your emulator is not running” or “ADB cannot see it.” Start an emulator from Android Studio Device Manager, then run:
adb devices
If nothing shows up, the React Native CLI cannot target anything.
Gradle errors that feel random
Gradle errors are loud and annoying, but the fix usually lives in the first real error line, not the bottom. Scroll up until you find the first failure that mentions a missing SDK, missing Java, or an incompatible version.
What you should do next (still small)
Once your baseline runs, keep the next move small. Add one button. Add one press handler. Change the layout slightly. The point is to keep the feedback loop alive while you learn the shape of React Native.
If you want a clean next step that still stays verifiable, add a button that toggles text so you confirm state updates in the app.
Replace App.tsx with this:
import React from "react";import { SafeAreaView, Text, Pressable, View } from "react-native";export default function App() {const [on, setOn] = React.useState(false);return (<SafeAreaView style={{ padding: 24 }}><View style={{ gap: 12 }}><Text style={{ fontSize: 18 }}>{on ? "State is ON" : "State is OFF"}</Text><PressableonPress={() => setOn((v) => !v)}style={{paddingVertical: 12,paddingHorizontal: 16,borderRadius: 10,borderWidth: 1,}}><Text>Toggle</Text></Pressable></View></SafeAreaView>);}
This is still minimal, but it proves something more than static rendering. It proves state changes render correctly, touch input works, and your development loop is stable.
Related links
The React Native docs have two different setup paths (Expo and React Native CLI). This post is the React Native CLI path because it forces you to prove the native toolchain works.
https://reactnative.dev/docs/environment-setup
Closing
If you can do these three things, you have a real starting point: create the project, run it, and change JavaScript and see the update on screen. Everything else builds on top of that.
Keep reading
- Sell Your Skills With One Small Project
- Portfolio Case Studies: A Simple, Verifiable Template
- Interactive Pokedex: Static Build With Search
References
- React Native — Getting Started
- React Native — Environment Setup
- React Native — SafeAreaView docs
- React Native — Text component docs
- React Native — Fast Refresh docs
- React Native — Troubleshooting
- Metro Bundler — Official site
- Node.js — Official site
- Xcode — Apple Developer
- Android Studio — Android Developers
- Android Studio — SDK configuration
- ADB — Android Debug Bridge
- Gradle — Java Compatibility
- CocoaPods — Official site
