🌐 Detecting your location…
📢 Advertisement — Configure AdSense in Appearance → Customize → AdSense Settings

React Native Tutorial 2026: Expo, Navigation and App Store Deployment

⏱️5 min read  ·  898 words

React Native in 2026 with the New Architecture (JSI + Fabric), Expo SDK 52, and React 19 support makes building production mobile apps faster than ever. If you already know React, React Native lets you build iOS and Android apps with 85-90% shared code. This tutorial covers the essentials.

Setup

# Using Expo (recommended for new projects)
npx create-expo-app@latest MyApp
cd MyApp
npx expo start

# Or bare workflow (more control, more setup)
npx @react-native-community/cli@latest init MyApp --new-architecture
cd MyApp
npx pod-install  # iOS only
npx react-native run-ios    # requires macOS + Xcode
npx react-native run-android  # requires Android Studio

Core Components

import {
  View, Text, StyleSheet, Pressable,
  ScrollView, FlatList, Image,
  TextInput, SafeAreaView, Platform,
} from 'react-native';

// No div, span, p — use View and Text instead
export default function HomeScreen() {
  return (
    <SafeAreaView style={styles.container}>
      <ScrollView>
        <Text style={styles.title}>Hello React Native!</Text>
        <Text style={styles.body}>
          This runs on iOS and Android natively.
        </Text>
        <Pressable
          style={({ pressed }) => [
            styles.button,
            pressed && styles.buttonPressed,
          ]}
          onPress={() => console.log('Pressed!')}
        >
          <Text style={styles.buttonText}>Tap Me</Text>
        </Pressable>
      </ScrollView>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
  },
  title: {
    fontSize: 28,
    fontWeight: 'bold',
    padding: 16,
    // Platform-specific
    ...Platform.select({
      ios: { fontFamily: 'SF Pro Display' },
      android: { fontFamily: 'Roboto' },
    }),
  },
  body: {
    fontSize: 16,
    color: '#555',
    paddingHorizontal: 16,
    lineHeight: 24,
  },
  button: {
    backgroundColor: '#0066CC',
    margin: 16,
    padding: 16,
    borderRadius: 12,
    alignItems: 'center',
  },
  buttonPressed: {
    opacity: 0.7,
    transform: [{ scale: 0.98 }],
  },
  buttonText: {
    color: 'white',
    fontWeight: '600',
    fontSize: 16,
  },
});

Navigation with React Navigation

npm install @react-navigation/native @react-navigation/native-stack
npm install react-native-screens react-native-safe-area-context

import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';

type RootStackParamList = {
  Home: undefined;
  Profile: { userId: number };
  Settings: undefined;
};

const Stack = createNativeStackNavigator<RootStackParamList>();

export default function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator
        initialRouteName="Home"
        screenOptions={{
          headerStyle: { backgroundColor: '#0066CC' },
          headerTintColor: 'white',
        }}
      >
        <Stack.Screen name="Home" component={HomeScreen} />
        <Stack.Screen name="Profile" component={ProfileScreen} />
        <Stack.Screen name="Settings" component={SettingsScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

// Navigate between screens
function HomeScreen({ navigation }) {
  return (
    <View>
      <Pressable onPress={() => navigation.navigate('Profile', { userId: 1 })}>
        <Text>Go to Profile</Text>
      </Pressable>
    </View>
  );
}

function ProfileScreen({ route, navigation }) {
  const { userId } = route.params;  // typed!
  return <Text>Profile {userId}</Text>;
}

Data Fetching

import { useQuery, useMutation, QueryClient, QueryClientProvider } from '@tanstack/react-query';

const queryClient = new QueryClient();

// Wrap app
export default function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <NavigationContainer>...</NavigationContainer>
    </QueryClientProvider>
  );
}

// Fetch users
function UserList() {
  const { data, isLoading, error } = useQuery({
    queryKey: ['users'],
    queryFn: () => fetch('https://api.example.com/users').then(r => r.json()),
  });

  if (isLoading) return <ActivityIndicator size="large" />;
  if (error) return <Text>Error: {error.message}</Text>;

  return (
    <FlatList
      data={data}
      keyExtractor={user => user.id.toString()}
      renderItem={({ item }) => (
        <View style={styles.userCard}>
          <Image source={{ uri: item.avatarUrl }} style={styles.avatar} />
          <Text style={styles.userName}>{item.name}</Text>
        </View>
      )}
    />
  );
}

Local Storage and State

npm install @react-native-async-storage/async-storage
npm install zustand  # lightweight state management

import AsyncStorage from '@react-native-async-storage/async-storage';
import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';

// Zustand store with AsyncStorage persistence
interface UserStore {
  user: User | null;
  token: string | null;
  setUser: (user: User, token: string) => void;
  logout: () => void;
}

export const useUserStore = create<UserStore>()(
  persist(
    (set) => ({
      user: null,
      token: null,
      setUser: (user, token) => set({ user, token }),
      logout: () => set({ user: null, token: null }),
    }),
    {
      name: 'user-storage',
      storage: createJSONStorage(() => AsyncStorage),
    }
  )
);

// Usage in component
function ProfileScreen() {
  const { user, logout } = useUserStore();
  return (
    <View>
      <Text>Welcome, {user?.name}!</Text>
      <Pressable onPress={logout}>
        <Text>Logout</Text>
      </Pressable>
    </View>
  );
}

Expo Router (File-based Routing)

// app/(tabs)/index.tsx  → "/" tab
// app/(tabs)/profile.tsx → "/profile" tab
// app/[id].tsx → dynamic route

// Expo Router — Next.js-style for mobile
import { Stack, Tabs } from 'expo-router';

export default function RootLayout() {
  return <Stack />;
}

// app/(tabs)/_layout.tsx
export default function TabLayout() {
  return (
    <Tabs>
      <Tabs.Screen name="index" options={{ title: 'Home' }} />
      <Tabs.Screen name="profile" options={{ title: 'Profile' }} />
    </Tabs>
  );
}

Publishing to App Stores

# Expo EAS (Expo Application Services) — recommended
npm install -g eas-cli
eas login
eas build:configure

# Build for production
eas build --platform ios      # builds .ipa for App Store
eas build --platform android  # builds .aab for Play Store
eas build --platform all      # both simultaneously

# Submit to stores
eas submit --platform ios
eas submit --platform android

React Native in 2026 is production-ready for most mobile app use cases. The New Architecture eliminates the old JavaScript bridge, making animations smoother and native module calls faster. Use Expo for faster development and simpler deployments — you can always eject to bare workflow if you need full native access. If you know React, React Native’s learning curve is manageable in 2-4 weeks.

✍️ Leave a Comment

Your email address will not be published. Required fields are marked *

🌐 Read in:🇬🇧 English🇩🇪 Deutsch🇧🇷 Português🇸🇦 العربية🇮🇳 हिन्दी🇧🇩 বাংলা