Create a News Reader app using React-Native
Creating the News Reader application using React-Native is an exciting project. Using this project, users can read news directly within the application, filtering it by categories of interest. In this article, we will develop the complete News Reader application to fetch real-time news from the News API.
To give you a better idea of what we’re going to create, let’s watch a demo video.
Demo Video
Prerequisites:
- Introduction to React Native
- React Native State
- React Native Props
- Expo CLI
- Node.js and npm (Node Package Manager)
Approach to create a News Reader App:
- This application is a single-page application.
- We have used Axios to make the asynchronous HTTP requests to the News API. Using this, the news is fetched based on the user's selected category, which also updates the app's state.
- When the user clicks on the news, the Modal is shown with the details of the news. The user can click on the link to visit the source of the news to get more insights from it.
- User can filter the news as per their interest by selecting the interest category from the Picker.
Step-by-Step Implementation
Step 1: Create a React Native Project
Now, create a project with the following command.
npx create-expo-app app-name --template
Note: Replace the app-name with your app name for example : react-native-demo-app
Next, you might be asked to choose a template. Select one based on your preference as shown in the image below. I am selecting the blank template because it will generate a minimal app that is as clean as an empty canvas in JavaScript.

It completes the project creation and displays a message: "Your Project is ready!" as shown in the image below.

Now go into your project folder, i.e., react-native-demo
cd app-name
Project Structure:

Step 2: Run Application
Start the server by using the following command.
npx expo start
Then, the application will display a QR code.
- For the Android users,
- For the Android Emulator, press " a" as mentioned in the image below.
- For the Physical Device, download the " Expo Go " app from the Play Store. Open the app, and you will see a button labeled " Scan QR Code. " Click that button and scan the QR code; it will automatically build the Android app on your device.
- For iOS users, simply scan the QR code using the Camera app.
- If you're using a web browser, it will provide a local host link that you can use as mentioned in the image below.

Step 3: Update Dependencies
The updated dependencies in the package.json file will look like:
"dependencies": {
"axios": "^1.10.0",
"expo": "53.0.13",
"expo-status-bar": "2.2.3",
"react": "18.2.0",
"react-native": "0.72.6",
"react-native-elements": "3.4.3",
"react-native-picker-select": "9.3.1"
}
Example: In this example, we are following the above-explained approach.
// App.js
import React, { useState, useEffect } from 'react';
import {
View, Text, FlatList, Modal, TouchableOpacity,
RefreshControl, ActivityIndicator, Linking
} from 'react-native';
import { Card, Icon, Overlay } from 'react-native-elements';
import RNPickerSelect from 'react-native-picker-select';
import { styles as appStyles, pickerSelectStyles } from './styles';
const App = () => {
const [news, setNews] = useState([]);
const [selectedNews, setSelectedNews] = useState(null);
const [modalVisible, setModalVisible] = useState(false);
const [selectedCategory, setSelectedCategory] =
useState('general');
const [refreshing, setRefreshing] = useState(false);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchNews();
}, [selectedCategory]);
const fetchNews = async () => {
try {
setLoading(true);
const response = await fetch(
`https://newsapi.org/v2/top-headlines?country=us&category=${selectedCategory}&apiKey=bd3916e8bfe04202b97dc9ad162b8483`
);
const result = await response.json();
setNews(
result.articles
.map(
article =>
(
{
...article,
category: selectedCategory
}
)));
} catch (error) {
console.error('Error fetching news:', error.message);
} finally {
setLoading(false);
}
};
const onRefresh = async () => {
setRefreshing(true);
try {
await fetchNews();
} finally {
setRefreshing(false);
}
};
const openNewsLink = () => {
if (selectedNews?.url) {
Linking.openURL(selectedNews.url);
}
};
const renderItem = ({ item }) => (
<Card containerStyle={appStyles.cardContainer}>
<Card.Title
style={appStyles.cardTitle}>
{item.title}
</Card.Title>
<Card.Image
source={{ uri: item.urlToImage }}
style={appStyles.cardImage} />
<Text
style={appStyles.description}>
{item.description}
</Text>
<View style={appStyles.cardFooter}>
<View style={appStyles.categoryContainer}>
<Icon name="tag"
type="font-awesome"
color="gray" size={16} />
<Text style={appStyles.categoryLabel}>
{item.category}
</Text>
</View>
<TouchableOpacity
style={appStyles.readMoreButton}
onPress={() => {
setSelectedNews(item);
setModalVisible(true);
}}
>
<Text style={appStyles.readMoreButtonText}>
Read more
</Text>
</TouchableOpacity>
</View>
</Card>
);
return (
<View style={appStyles.container}>
<View style={appStyles.headerContainer}>
<Icon name="newspaper-o"
type="font-awesome"
color="green" size={30} />
<Text style={appStyles.header}>
GeeksforGeeks News Reader
</Text>
</View>
<View style={appStyles.categoryPickerContainer}>
<Text style={appStyles.categoryPickerLabel}>
Select Category:
</Text>
<RNPickerSelect
placeholder={{}}
onValueChange={
(itemValue) =>
setSelectedCategory(itemValue)
}
items={[
{ label: 'General', value: 'general' },
{ label: 'Business', value: 'business' },
{ label: 'Technology', value: 'technology' },
{ label: 'Sports', value: 'sports' },
]}
style={pickerSelectStyles}
/>
</View>
{loading ? (
<ActivityIndicator size="large"
color="#3498db"
style={{ marginTop: 20 }} />
) : (
<FlatList
data={news}
renderItem={renderItem}
keyExtractor={(item) => item.url}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={onRefresh} />
}
/>
)}
<Modal animationType="slide"
transparent={false} visible={modalVisible}>
<Overlay isVisible={modalVisible}
overlayStyle={appStyles.modalContainer}
onBackdropPress={
() => setModalVisible(false)
}>
<Card containerStyle={appStyles.modalCard}>
<Card.Title style={appStyles.cardTitle}>
{selectedNews?.title}
</Card.Title>
<Card.Image source={{ uri: selectedNews?.urlToImage }}
style={appStyles.cardImage} />
<Text>{selectedNews?.content}</Text>
<TouchableOpacity style={appStyles.readMoreButton}
onPress={openNewsLink}>
<Text style={appStyles.readMoreButtonText}>
Read Full Article
</Text>
</TouchableOpacity>
</Card>
</Overlay>
</Modal>
</View>
);
};
export default App;
//styles.js
import { StyleSheet } from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#ecf0f1',
padding: 10,
},
headerContainer: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 10,
paddingTop: 50,
},
header: {
fontSize: 24,
fontWeight: 'bold',
marginLeft: 10,
},
categoryPickerContainer: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 10,
},
categoryPickerLabel: {
fontSize: 16,
marginRight: 10,
},
cardContainer: {
borderRadius: 10,
marginBottom: 15,
overflow: 'hidden',
},
cardTitle: {
fontSize: 18,
fontWeight: 'bold',
},
cardImage: {
height: 200,
resizeMode: 'cover',
borderRadius: 10,
},
categories: {
flexDirection: 'row',
justifyContent: 'space-around',
marginBottom: 10,
flexWrap: 'wrap',
},
categoryContainer: {
flexDirection: 'row',
alignItems: 'center',
},
categoryLabel: {
color: 'gray',
marginLeft: 5,
},
description: {
marginVertical: 10,
},
cardFooter: {
flexDirection: 'row',
justifyContent: 'space-between',
marginTop: 10,
alignItems: 'center',
},
readMoreButton: {
backgroundColor: '#3498db',
padding: 8,
borderRadius: 5,
marginTop: 10,
alignItems: 'center',
},
readMoreButtonText: {
color: 'white',
fontWeight: 'bold',
},
modalContainer: {
padding: 20,
},
modalCard: {
borderRadius: 10,
overflow: 'hidden',
},
});
const pickerSelectStyles = StyleSheet.create({
inputIOS: {
fontSize: 16,
paddingVertical: 12,
paddingHorizontal: 10,
borderWidth: 1,
borderColor: 'gray',
borderRadius: 5,
color: 'black',
paddingRight: 30,
},
inputAndroid: {
fontSize: 16,
paddingHorizontal: 10,
paddingVertical: 8,
borderWidth: 0.5,
borderColor: 'purple',
borderRadius: 8,
color: 'black',
paddingRight: 30,
},
});
export { styles, pickerSelectStyles };