What You'll Build
A Home screen with a city text field; submitting navigates to a Details screen that fetches and displays that city's current weather, with loading and error states handled explicitly.
Prerequisites
- A React Native (Expo recommended) project with
@react-navigation/nativeand@react-navigation/native-stackinstalled - A free API key from OpenWeatherMap
- Read Networking with fetch and Navigation (Stack Navigator) first if these are new to you
Step by Step
1 Set up the navigator
// App.js
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import HomeScreen from './HomeScreen';
import DetailsScreen from './DetailsScreen';
const Stack = createNativeStackNavigator();
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} options={{ title: 'Weather' }} />
<Stack.Screen name="Details" component={DetailsScreen} options={{ title: 'Forecast' }} />
</Stack.Navigator>
</NavigationContainer>
);
}
2 Build the Home screen
Passing the city as a route param keeps the Details screen decoupled from Home — it doesn't need to know how the city was chosen.
// HomeScreen.js
import React, { useState } from 'react';
import { View, TextInput, Pressable, Text, StyleSheet } from 'react-native';
export default function HomeScreen({ navigation }) {
const [city, setCity] = useState('');
return (
<View style={styles.container}>
<TextInput
style={styles.input}
placeholder="City name"
value={city}
onChangeText={setCity}
/>
<Pressable
style={styles.button}
onPress={() => navigation.navigate('Details', { city })}
disabled={!city.trim()}
>
<Text style={styles.buttonText}>Get Weather</Text>
</Pressable>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, padding: 20, justifyContent: 'center' },
input: { borderWidth: 1, borderColor: '#ccc', borderRadius: 8, padding: 12, marginBottom: 12 },
button: { backgroundColor: '#1c8fb0', borderRadius: 8, padding: 14, alignItems: 'center' },
buttonText: { color: '#fff', fontWeight: 'bold' },
});
3 Build the Details screen with fetch
// DetailsScreen.js
import React, { useState, useEffect } from 'react';
import { View, Text, ActivityIndicator, StyleSheet } from 'react-native';
const API_KEY = 'YOUR_API_KEY';
export default function DetailsScreen({ route }) {
const { city } = route.params;
const [status, setStatus] = useState('loading'); // 'loading' | 'success' | 'error'
const [weather, setWeather] = useState(null);
useEffect(() => {
let cancelled = false;
async function loadWeather() {
try {
const url = `https://api.openweathermap.org/data/2.5/weather?q=${encodeURIComponent(city)}&appid=${API_KEY}&units=metric`;
const res = await fetch(url);
if (!res.ok) throw new Error('Request failed');
const data = await res.json();
if (!cancelled) {
setWeather(data);
setStatus('success');
}
} catch (e) {
if (!cancelled) setStatus('error');
}
}
loadWeather();
return () => { cancelled = true; }; // avoid setting state after unmount
}, [city]);
if (status === 'loading') {
return <View style={styles.center}><ActivityIndicator size="large" /></View>;
}
if (status === 'error') {
return (
<View style={styles.center}>
<Text>⚠️ Couldn't load weather for "{city}". Check the spelling and try again.</Text>
</View>
);
}
return (
<View style={styles.center}>
<Text style={styles.city}>{weather.name}</Text>
<Text style={styles.temp}>{Math.round(weather.main.temp)}°C</Text>
<Text>{weather.weather[0]?.description}</Text>
<Text>Humidity: {weather.main.humidity}%</Text>
</View>
);
}
const styles = StyleSheet.create({
center: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: 20 },
city: { fontSize: 24, fontWeight: 'bold' },
temp: { fontSize: 48, fontWeight: '200', marginVertical: 8 },
});
The cancelled flag prevents a "can't update state on an unmounted component" warning if the user navigates back before the request finishes.
4 Run it
Start the app, type a city on the Home screen, tap "Get Weather" — you should see a spinner, then either the forecast or an error message. Try navigating back and forward again to confirm the fetch re-runs for a new city.
Final Working Code
Three files — App.js, HomeScreen.js, DetailsScreen.js — exactly as shown above form the complete, working app.