Open In App

Create a News Reader app using React-Native

Last Updated : 30 Jun, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

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:

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
// 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
//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 };

Output:


Next Article

Similar Reads