React Native + Expo SDK 52 ist 2026 der Standard für produktionsreife mobile Apps —
ein gemeinsamer TypeScript-Code für iOS und Android, native Performance und direkter Zugriff auf
Kamera, GPS, Push-Benachrichtigungen und sichere Schlüsselverwaltung. Claude Code kennt die gesamte
Expo-Ökosystem und generiert typsichere, getestete App-Architektur in wenigen Minuten.
React Native
Expo SDK 52
Expo Router
Reanimated 3
EAS Build
TypeScript
Warum React Native?
Ein TypeScript-Codebase für iOS und Android. Native UI-Komponenten, keine WebView.
Warum Expo?
Expo SDK, EAS Build, OTA-Updates und file-basiertes Routing mit Expo Router out-of-the-box.
Claude Code Vorteil
Generiert Screens, Hooks, Native API-Integration und vollständige EAS-Konfiguration.
2026 Stack
Expo SDK 52, Expo Router v4, Reanimated 3, NativeWind v4, EAS Build + Submit.
1. Expo Grundlagen: Projekt-Setup und Architektur
Mit Expo startet ein neues React Native Projekt in Sekunden. Das create-expo-app-Template
legt die komplette Ordnerstruktur, TypeScript-Konfiguration und das Metro Bundler-Setup automatisch an.
Claude Code generiert zusätzlich eas.json, app.config.ts und den Dev-Client-Workflow.
Neues Expo-Projekt erstellen
# Neues Projekt mit Expo Router Template
npx create-expo-app@latest MeineApp --template tabs
# In Projektverzeichnis wechseln
cd MeineApp
# Expo Dev Client installieren (für native Modules)
npx expo install expo-dev-client
# Entwicklungsserver starten
npx expo start --dev-client
# EAS CLI global installieren
npm install -g eas-cli
eas login
app.config.ts — Typsichere App-Konfiguration
import { ExpoConfig, ConfigContext } from 'expo/config';
export default ({ config }: ConfigContext): ExpoConfig => ({
...config,
name: 'MeineApp',
slug: 'meine-app',
version: '1.0.0',
orientation: 'portrait',
icon: './assets/images/icon.png',
scheme: 'meineapp',
userInterfaceStyle: 'automatic',
splash: {
image: './assets/images/splash-icon.png',
resizeMode: 'contain',
backgroundColor: '#0f172a',
},
ios: {
supportsTablet: true,
bundleIdentifier: 'com.company.meineapp',
infoPlist: {
NSCameraUsageDescription: 'Für Profilfotos und QR-Scans',
NSLocationWhenInUseUsageDescription: 'Für standortbasierte Inhalte',
},
},
android: {
adaptiveIcon: {
foregroundImage: './assets/images/adaptive-icon.png',
backgroundColor: '#0f172a',
},
package: 'com.company.meineapp',
permissions: [
'CAMERA',
'ACCESS_FINE_LOCATION',
'RECEIVE_BOOT_COMPLETED',
],
},
plugins: [
'expo-router',
'expo-secure-store',
['expo-camera', { cameraPermission: 'Kamerazugriff für QR-Code-Scanner' }],
],
experiments: {
typedRoutes: true,
},
extra: {
apiUrl: process.env.EXPO_PUBLIC_API_URL,
eas: { projectId: process.env.EAS_PROJECT_ID },
},
});
app/_layout.tsx — Root Layout
import { Stack } from 'expo-router';
import { StatusBar } from 'expo-status-bar';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ThemeProvider } from '@react-navigation/native';
import { DarkTheme, DefaultTheme } from '@react-navigation/native';
import { useColorScheme } from 'react-native';
const queryClient = new QueryClient();
export default function RootLayout() {
const colorScheme = useColorScheme();
return (
<QueryClientProvider client={queryClient}>
<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
<StatusBar style="auto" />
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="(auth)" options={{ headerShown: false }} />
<Stack.Screen name="modal" options={{ presentation: 'modal' }} />
</Stack>
</ThemeProvider>
</QueryClientProvider>
);
}
Metro Bundler: Konfiguration für Monorepos
- metro.config.js — watchFolders für Workspace-Packages
- resolver.nodeModulesPaths für geteilte Abhängigkeiten
- transformer.unstable_allowRequireContext für Expo Router
- cacheVersion bei größeren Config-Änderungen inkrementieren
EXPO_USE_FAST_RESOLVER=1 — schnellere Module-Auflösung
2. Navigation: Expo Router und File-Based Routing
Expo Router v4 bringt file-based Routing direkt in React Native — analog zu Next.js App Router.
Jede Datei im app/-Verzeichnis wird zu einer Route. Stack-Navigation, Tab-Navigation und Drawer-Navigation
lassen sich einfach kombinieren. Mit typedRoutes in app.config.ts sind alle Routen vollständig typisiert.
Ordnerstruktur — File-Based Routing
app/
├── _layout.tsx # Root Layout (QueryClient, Theme)
├── (auth)/
│ ├── _layout.tsx # Auth Stack Layout
│ ├── login.tsx # Route: /login
│ └── register.tsx # Route: /register
├── (tabs)/
│ ├── _layout.tsx # Tab Bar Layout
│ ├── index.tsx # Route: / (Home Tab)
│ ├── explore.tsx # Route: /explore
│ └── profil.tsx # Route: /profil
├── produkt/
│ └── [id].tsx # Route: /produkt/:id (Dynamic)
├── [...missing].tsx # 404 Fallback
└── modal.tsx # Route: /modal (Modal Presentation)
Tab-Navigation mit Expo Router
import { Tabs } from 'expo-router';
import { Ionicons } from '@expo/vector-icons';
import { useColorScheme } from 'react-native';
import Colors from '@/constants/Colors';
type IoniconName = keyof typeof Ionicons.glyphMap;
function TabBarIcon({
name, color
}: {
name: IoniconName;
color: string;
}) {
return <Ionicons name={name} size={24} color={color} />;
}
export default function TabsLayout() {
const colorScheme = useColorScheme();
const tint = Colors[colorScheme ?? 'light'].tint;
return (
<Tabs
screenOptions={{
tabBarActiveTintColor: tint,
headerShown: false,
tabBarStyle: {
backgroundColor: colorScheme === 'dark' ? '#1e293b' : '#fff',
borderTopColor: colorScheme === 'dark' ? '#334155' : '#e2e8f0',
},
}}
>
<Tabs.Screen
name="index"
options={{
title: 'Home',
tabBarIcon: ({ color }) => <TabBarIcon name="home" color={color} />,
}}
/>
<Tabs.Screen
name="explore"
options={{
title: 'Erkunden',
tabBarIcon: ({ color }) => <TabBarIcon name="compass" color={color} />,
}}
/>
<Tabs.Screen
name="profil"
options={{
title: 'Profil',
tabBarIcon: ({ color }) => <TabBarIcon name="person" color={color} />,
}}
/>
</Tabs>
);
}
useRouter und useLocalSearchParams
// app/produkt/[id].tsx — Dynamische Route
import { View, Text, Pressable, StyleSheet } from 'react-native';
import { useLocalSearchParams, useRouter } from 'expo-router';
import { useQuery } from '@tanstack/react-query';
export default function ProduktScreen() {
const { id } = useLocalSearchParams<{ id: string }>();
const router = useRouter();
const { data: produkt, isLoading } = useQuery({
queryKey: ['produkt', id],
queryFn: () => fetchProdukt(id),
enabled: !!id,
});
if (isLoading) return <LoadingSpinner />;
return (
<View style={styles.container}>
<Text style={styles.title}>{produkt?.name}</Text>
<Pressable
style={styles.btn}
onPress={() => router.push(`/warenkorb?add=${id}`)}
>
<Text style={styles.btnText}>In den Warenkorb</Text>
</Pressable>
<Pressable onPress={() => router.back()}>
<Text>Zurück</Text>
</Pressable>
</View>
);
}
Expo Router: Typsichere Routen mit TypeScript
experiments.typedRoutes: true in app.config.ts aktivieren
- Generierte Types in
.expo/types/router.d.ts — automatisch von Expo Router
router.push('/produkt/42') — TypeScript prüft alle Routen-Pfade
href-Prop bei <Link> ist vollständig typisiert
- Relative Navigation:
router.push('../settings') — funktioniert auch in verschachtelten Layouts
3. Native APIs: Kamera, GPS, Notifications und SecureStore
Expo SDK 52 liefert über 50 native Module — von der Kamera bis zum biometrischen Sensor.
Claude Code generiert den vollständigen Permission-Flow, Error-Handling und Plattform-spezifische
Fallbacks für iOS und Android. Alle Module sind mit expo install versionssicher installierbar.
expo-camera: QR-Code-Scanner
import { useState, useEffect } from 'react';
import { View, Text, StyleSheet, Alert } from 'react-native';
import { CameraView, Camera, BarcodeScanningResult } from 'expo-camera';
export function QRScanner({ onScan }: { onScan: (data: string) => void }) {
const [hasPermission, setHasPermission] = useState<boolean | null>(null);
const [scanned, setScanned] = useState(false);
useEffect(() => {
(async () => {
const { status } = await Camera.requestCameraPermissionsAsync();
setHasPermission(status === 'granted');
})();
}, []);
const handleBarCodeScanned = ({ data }: BarcodeScanningResult) => {
setScanned(true);
onScan(data);
Alert.alert('QR-Code gescannt', data, [
{ text: 'Erneut scannen', onPress: () => setScanned(false) },
]);
};
if (hasPermission === null) return <Text>Kamerazugriff wird angefragt…</Text>;
if (!hasPermission) return <Text>Kamerazugriff verweigert</Text>;
return (
<View style={styles.container}>
<CameraView
style={StyleSheet.absoluteFillObject}
facing="back"
barcodeScannerSettings={{ barcodeTypes: ['qr', 'ean13'] }}
onBarcodeScanned={scanned ? undefined : handleBarCodeScanned}
/>
<View style={styles.overlay}>
<View style={styles.scanFrame} />
<Text style={styles.hint}>QR-Code im Rahmen positionieren</Text>
</View>
</View>
);
}
expo-location: Standortbasierte Services
import * as Location from 'expo-location';
import { useEffect, useState } from 'react';
interface Koordinaten {
latitude: number;
longitude: number;
accuracy: number | null;
}
export function useStandort() {
const [koordinaten, setKoordinaten] = useState<Koordinaten | null>(null);
const [fehler, setFehler] = useState<string | null>(null);
const [laden, setLaden] = useState(true);
useEffect(() => {
let subscription: Location.LocationSubscription;
(async () => {
const { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
setFehler('Standortzugriff wurde verweigert');
setLaden(false);
return;
}
subscription = await Location.watchPositionAsync(
{
accuracy: Location.Accuracy.High,
timeInterval: 5000,
distanceInterval: 10,
},
(location) => {
setKoordinaten({
latitude: location.coords.latitude,
longitude: location.coords.longitude,
accuracy: location.coords.accuracy,
});
setLaden(false);
}
);
})();
return () => subscription?.remove();
}, []);
return { koordinaten, fehler, laden };
}
expo-secure-store und expo-notifications
import * as SecureStore from 'expo-secure-store';
import * as Notifications from 'expo-notifications';
import { Platform } from 'react-native';
// SecureStore: Token sicher speichern (Keychain / Keystore)
export const TokenManager = {
async speichern(token: string): Promise<void> {
await SecureStore.setItemAsync('auth_token', token);
},
async laden(): Promise<string | null> {
return SecureStore.getItemAsync('auth_token');
},
async loeschen(): Promise<void> {
await SecureStore.deleteItemAsync('auth_token');
},
};
// Push-Benachrichtigungen registrieren
export async function pushTokenAnfordern(): Promise<string | null> {
if (Platform.OS === 'android') {
await Notifications.setNotificationChannelAsync('default', {
name: 'Standard',
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
});
}
const { status } = await Notifications.requestPermissionsAsync();
if (status !== 'granted') return null;
const token = await Notifications.getExpoPushTokenAsync({
projectId: process.env.EXPO_PUBLIC_PROJECT_ID!,
});
return token.data;
}
Platform.OS — Plattformspezifische Logik
Platform.OS === 'ios' | 'android' | 'web' — Runtime-Check
Platform.select({'{'} ios: StyleA, android: StyleB {'}'} ) — Style-Auswahl
- Dateiendungen:
Komponente.ios.tsx und Komponente.android.tsx — Metro lädt automatisch
Platform.Version — iOS: Versionsstring, Android: API-Level als Zahl
- Safe Area:
expo-linear-gradient + react-native-safe-area-context für Notch/Statusbar
4. Styling & UI: StyleSheet, NativeWind und react-native-paper
React Native-Styling erfolgt über StyleSheet.create() — ein optimierter, plattformneutraler
Ansatz der CSS-ähnliche Eigenschaften mit JavaScript-Objekten kombiniert. NativeWind v4
bringt Tailwind CSS-Klassen auf mobile, während react-native-paper Material Design 3
Komponenten liefert.
StyleSheet und Flexbox
import { View, Text, Pressable, StyleSheet, Platform } from 'react-native';
interface KarteProps {
titel: string;
beschreibung: string;
onDruck: () => void;
}
export function ProduktKarte({ titel, beschreibung, onDruck }: KarteProps) {
return (
<Pressable
style={({ pressed }) => [styles.karte, pressed && styles.gedrueckt]}
onPress={onDruck}
>
<View style={styles.kopf}>
<Text style={styles.titel}>{titel}</Text>
</View>
<Text style={styles.text}>{beschreibung}</Text>
</Pressable>
);
}
const styles = StyleSheet.create({
karte: {
backgroundColor: '#1e293b',
borderRadius: 12,
padding: 16,
marginVertical: 8,
marginHorizontal: 16,
// Plattformspezifischer Schatten
...Platform.select({
ios: {
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.15,
shadowRadius: 8,
},
android: {
elevation: 4,
},
}),
},
gedrueckt: {
opacity: 0.75,
transform: [{ scale: 0.98 }],
},
kopf: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
marginBottom: 8,
},
titel: {
fontSize: 16,
fontWeight: '700',
color: '#f1f5f9',
flex: 1,
},
text: {
fontSize: 14,
color: '#94a3b8',
lineHeight: 20,
},
});
NativeWind v4: Tailwind für React Native
// babel.config.js
module.exports = {
presets: ['babel-preset-expo'],
plugins: ['nativewind/babel'],
};
// Komponente mit NativeWind-Klassen
import { View, Text, Pressable } from 'react-native';
import { styled } from 'nativewind';
const StyledView = styled(View);
const StyledText = styled(Text);
const StyledPressable = styled(Pressable);
export function LoginFormular() {
return (
<StyledView className="flex-1 bg-slate-900 items-center justify-center px-6">
<StyledText className="text-3xl font-bold text-white mb-8">
Anmelden
</StyledText>
<StyledView className="w-full bg-slate-800 rounded-xl p-4 mb-4">
<StyledText className="text-slate-400 text-sm mb-1">E-Mail</StyledText>
<{/* TextInput hier */}>
</StyledView>
<StyledPressable
className="w-full bg-indigo-500 rounded-xl py-4 items-center active:opacity-75"
>
<StyledText className="text-white font-bold text-base">Einloggen</StyledText>
</StyledPressable>
</StyledView>
);
}
| Styling-Ansatz |
Vorteil |
Nachteil |
Empfehlung |
| StyleSheet.create() |
Native Performance, typsicher |
Verbose, kein CSS-Nesting |
Basis für alle Apps |
| NativeWind v4 |
Tailwind-Workflow, Dark Mode |
Build-Overhead |
Utility-First Teams |
| react-native-paper |
Material 3, fertige Komponenten |
Bundle-Größe |
Enterprise / Material Design |
| Tamagui |
Universal (RN + Web), schnell |
Komplexes Setup |
Fullstack / Expo + Next.js |
5. Animationen: react-native-reanimated 3 und Gesture Handler
react-native-reanimated 3 führt Animationen auf dem nativen UI-Thread aus —
komplett unabhängig vom JavaScript-Thread. Damit sind butterweiche 60/120fps-Animationen auch bei
rechenintensiven JS-Operationen möglich. Kombiniert mit react-native-gesture-handler
entstehen intuitive Swipe-, Drag- und Pinch-Interaktionen.
useSharedValue und useAnimatedStyle
import Animated, {
useSharedValue,
useAnimatedStyle,
withSpring,
withTiming,
withRepeat,
withSequence,
Easing,
} from 'react-native-reanimated';
import { Pressable } from 'react-native';
export function AnimierterButton({ onPress, kinder }: { onPress: () => void; kinder: React.ReactNode }) {
const scale = useSharedValue(1);
const opacity = useSharedValue(1);
const animierterStyle = useAnimatedStyle(() => ({
transform: [{ scale: scale.value }],
opacity: opacity.value,
}));
const handlePressIn = () => {
scale.value = withSpring(0.95, { damping: 15, stiffness: 300 });
opacity.value = withTiming(0.8, { duration: 100 });
};
const handlePressOut = () => {
scale.value = withSpring(1, { damping: 10, stiffness: 200 });
opacity.value = withTiming(1, { duration: 150 });
};
return (
<Animated.View style={animierterStyle}>
<Pressable
onPress={onPress}
onPressIn={handlePressIn}
onPressOut={handlePressOut}
>
{kinder}
</Pressable>
</Animated.View>
);
}
Gesture Handler: Swipeable Karten
import { GestureDetector, Gesture } from 'react-native-gesture-handler';
import Animated, {
useSharedValue,
useAnimatedStyle,
withSpring,
runOnJS,
} from 'react-native-reanimated';
const SWIPE_THRESHOLD = 120;
export function SwipeableKarte({
kinder,
onSwipeLinks,
onSwipeRechts,
}: {
kinder: React.ReactNode;
onSwipeLinks: () => void;
onSwipeRechts: () => void;
}) {
const offsetX = useSharedValue(0);
const rotation = useSharedValue(0);
const pan = Gesture.Pan()
.onUpdate((e) => {
offsetX.value = e.translationX;
rotation.value = e.translationX * 0.05;
})
.onEnd((e) => {
if (e.translationX < -SWIPE_THRESHOLD) {
runOnJS(onSwipeLinks)();
} else if (e.translationX > SWIPE_THRESHOLD) {
runOnJS(onSwipeRechts)();
} else {
// Zurück zur Mitte springen
offsetX.value = withSpring(0, { damping: 12 });
rotation.value = withSpring(0, { damping: 12 });
}
});
const animierterStyle = useAnimatedStyle(() => ({
transform: [
{ translateX: offsetX.value },
{ rotate: `${rotation.value}deg` },
],
}));
return (
<GestureDetector gesture={pan}>
<Animated.View style={animierterStyle}>
{kinder}
</Animated.View>
</GestureDetector>
);
}
Shared Element Transitions und Layout Animationen
import Animated, {
FadeInDown,
FadeOutUp,
SlideInRight,
Layout,
ZoomIn,
} from 'react-native-reanimated';
import { FlatList } from 'react-native';
function AnimierteListeItem({ item, index }: { item: Produkt; index: number }) {
return (
<Animated.View
entering={FadeInDown.delay(index * 80).springify()}
exiting={FadeOutUp}
layout={Layout.springify()}
>
<ProduktKarte {...item} />
</Animated.View>
);
}
// Lottie-Animation mit expo-av Alternative
import LottieView from 'lottie-react-native';
export function LadeAnimation() {
return (
<LottieView
source={require('@/assets/animations/loading.json')}
autoPlay
loop
style={{ width: 120, height: 120 }}
/>
);
}
Reanimated: Häufige Patterns
runOnJS(fn)() — JS-Callbacks aus Reanimated Worklets aufrufen
useAnimatedScrollHandler — parallax Headers, sticky Elemente
useAnimatedKeyboard — weiche Keyboard-Avoid-Animation
interpolate() — Werte mappen (z. B. Scroll → Opacity)
cancelAnimation() — laufende Animation abbrechen
- Worklets MÜSSEN
'worklet' am Anfang haben oder als Arrow Function übergeben werden
6. EAS Build & Deploy: App Store und Google Play
Expo Application Services (EAS) automatisiert den gesamten Build- und Deployment-Prozess.
eas build kompiliert native Binaries in der Cloud, eas submit lädt direkt in
App Store Connect und Google Play hoch. OTA-Updates über expo-updates
ermöglichen sofortige JavaScript-Updates ohne App-Store-Review.
eas.json — Build-Profile
{
"cli": {
"version": ">= 12.0.0",
"appVersionSource": "remote"
},
"build": {
"development": {
"developmentClient": true,
"distribution": "internal",
"ios": { "simulator": true },
"android": { "buildType": "apk" }
},
"preview": {
"distribution": "internal",
"channel": "preview",
"android": { "buildType": "apk" }
},
"production": {
"autoIncrement": true,
"channel": "production",
"ios": { "resourceClass": "m-medium" },
"android": { "buildType": "app-bundle" }
}
},
"submit": {
"production": {
"ios": {
"appleId": "app@firma.de",
"ascAppId": "1234567890",
"appleTeamId": "ABCDE12345"
},
"android": {
"serviceAccountKeyPath": "./google-service-account.json",
"track": "internal"
}
}
}
}
EAS Build Commands
# Alle Plattformen bauen (Cloud)
eas build --platform all --profile production
# Nur Android (schneller für Tests)
eas build --platform android --profile preview
# Lokaler iOS-Build (benötigt Mac + Xcode)
eas build --platform ios --profile development --local
# In App Stores einreichen
eas submit --platform all --latest
# Build-Status prüfen
eas build:list --limit 5
# App Store Connect API Key einrichten
eas credentials -p ios
OTA Updates mit expo-updates
import * as Updates from 'expo-updates';
import { useEffect } from 'react';
import { Alert } from 'react-native';
export function useUpdatePruefer() {
useEffect(() => {
async function pruefeAufUpdates() {
if (__DEV__) return; // Im Dev-Mode überspringen
try {
const update = await Updates.checkForUpdateAsync();
if (update.isAvailable) {
await Updates.fetchUpdateAsync();
Alert.alert(
'Update verfügbar',
'Eine neue Version wurde geladen. App jetzt neu starten?',
[
{ text: 'Später', style: 'cancel' },
{
text: 'Jetzt neustarten',
onPress: () => Updates.reloadAsync(),
},
]
);
}
} catch (e) {
console.error('Update-Prüfung fehlgeschlagen:', e);
}
}
pruefeAufUpdates();
}, []);
}
// OTA-Update pushen (ohne App-Store-Review)
# eas update --channel production --message "Bugfix: Login-Flow"
CI/CD mit GitHub Actions + EAS
# .github/workflows/eas-build.yml
name: EAS Build
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
name: EAS Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- name: Install dependencies
run: npm ci
- name: Setup EAS
uses: expo/expo-github-action@v8
with:
eas-version: latest
token: ${{ secrets.EXPO_TOKEN }}
- name: Build (Preview)
run: eas build --platform android --profile preview --non-interactive
- name: Update (OTA)
if: github.ref == 'refs/heads/main'
run: eas update --channel production --message "${{ github.event.head_commit.message }}" --non-interactive
App-Store-Readiness-Checkliste
- Privacy Manifest (iOS 17+):
PrivacyInfo.xcprivacy mit NSPrivacyTracking, NSPrivacyAccessedAPITypes
- App Tracking Transparency:
expo-tracking-transparency für Analytics
- Adaptive Icons (Android): 108x108dp Foreground + Background Layer
- Screenshots: 6.7" iPhone + 12.9" iPad + Android (1080x1920, 2048x2732)
- Deep Links:
scheme in app.config.ts + Associated Domains (iOS) / App Links (Android)
- In-App Purchases:
expo-in-app-purchases oder RevenueCat SDK
- Crashlytics:
@react-native-firebase/crashlytics für Produktions-Monitoring
Claude Code Workflow: React Native App von 0 auf Production
- 1. Prompt: "Erstelle eine React Native Expo App mit Auth, Home Tab und Profil-Screen"
- 2. Claude generiert: Projektstruktur, Expo Router-Layouts, TypeScript-Interfaces
- 3. Native APIs: "Füge QR-Scanner, Push-Notifications und SecureStore hinzu"
- 4. Styling: "Konvertiere auf NativeWind v4 mit Dark Mode Support"
- 5. Animationen: "Implementiere Swipeable Cards und animierte Tab-Bar"
- 6. Deployment: "Erstelle eas.json und GitHub Actions Workflow für EAS Build"
- 7. Resultat: Produktionsreife App für iOS und Android in Stunden statt Wochen
React Native Apps mit Claude Code entwickeln
Expo Router, Native APIs, Reanimated und EAS Build — Claude Code kennt den kompletten
React Native Stack 2026 und generiert produktionsreife TypeScript-Apps für iOS und Android.
Jetzt kostenlos starten und die erste Mobile-App entwickeln lassen.
14 Tage kostenlos testen →