This article explains how to build a simple blog app using React Native that allows users to create and manage their blog posts. It helps you understand how to design interactive mobile interfaces and handle user input effectively.
- Add, edit, and delete blog posts easily within the app.
- Validate user input to ensure each post has a title and content.
- Learn React Native concepts like components, state management, and dynamic list rendering.
Approach
In this code, we are making a simple React Native app for a blog.
- It lets users look at a list of blog posts, choose a post to read, and add new posts to the list.
- Users can also edit or delete existing posts.
- The code checks to make sure that new posts have both a title and content and are not empty.
- It uses different React Native components like View, Text, TextInput, Button, and FlatList to show the blog posts and handle user actions.
- The styling is done carefully using StyleSheet for a neat appearance.
Here, We start implementing the React Native Blog App App step by step, from setup to task management features.
Step 1:Â Create a React Native Project
Now, create a project with the following command.
npx create-expo-app app-name --templateNote: 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-nameProject Structure

Step 2: Run  Application
Start the server by using the following command.
npx expo startThen, the application will display a QR code.
1. 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.
2. For iOS users, simply scan the QR code using the Camera app.
3. 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: Start Coding
Import libraries: Import required libraries at the top of the file.
// Import the useState hook from React for managing state in functional components
import { useState } from 'react';
// Import core React Native components for building the UI
import {
// Container component for layout
View,
// Button component for user actions
Button,
// Input field for text entry
TextInput,
// Component for displaying text
Text,
// Efficient list view for rendering posts
FlatList,
// Utility for creating component styles
StyleSheet,
// Wrapper for making views touchable
TouchableOpacity
} from 'react-native';
StyleSheet: Create a StyleSheet to style components like container, headingContainer, heading, etc.
// Define styles for the components using StyleSheet.create
const styles = StyleSheet.create({
// Main container style for the app
container: {
flex: 1,
paddingTop: 40,
paddingHorizontal: 20,
},
// Container for the heading section
headingContainer: {
backgroundColor: '#3498db',
padding: 10,
borderRadius: 10,
marginBottom: 20,
},
// Style for the main heading text
heading: {
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
color: 'white',
},
// Container for each blog post
postContainer: {
borderWidth: 1,
borderColor: '#ccc',
padding: 20,
marginBottom: 20,
borderRadius: 10,
},
// Style for the post title
postTitle: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 10,
},
// Style for the post content text
postContent: {
fontSize: 14,
textAlign: 'left',
},
// Style for the delete/edit button container
deleteButton: {
alignSelf: 'flex-end',
marginTop: 10,
},
// Style for the edit button text
editButtonText: {
color: 'green',
},
// Style for the delete button text
deleteButtonText: {
color: 'red',
},
// Container for the selected post view
selectedPostContainer: {
padding: 20,
marginBottom: 20,
borderWidth: 1,
borderColor: '#ccc',
borderRadius: 10,
},
// Style for the selected post's title
selectedPostTitle: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 10,
},
// Style for the selected post's content
selectedPostContent: {
fontSize: 16,
textAlign: 'justify',
},
// Style for the back button in the selected post view
backButton: {
alignSelf: 'flex-end',
marginTop: 20,
},
// Style for the back button text
backButtonText: {
color: 'blue',
fontSize: 16, // Medium font size
},
// Container for the form (add/edit post)
formContainer: {
padding: 20,
borderWidth: 1,
borderColor: '#ccc',
borderRadius: 10,
marginBottom: 20,
},
// Style for text input fields
input: {
borderWidth: 1,
borderColor: '#ccc',
padding: 10,
marginBottom: 10,
borderRadius: 5,
},
// Additional style for multi-line text area
textArea: {
height: 100,
},
// Style for error messages
errorText: {
color: 'red',
textAlign: 'center',
marginBottom: 10,
},
});
data: Create a global data variable that consists of a list of id, title, and content.
// Define initial blog post data as an array of objects
const data = [
{
id: 1, // Unique identifier for the post
title: 'React', // Title of the post
content: `ReactJS is a declarative, efficient, and flexible JavaScript library for building user interfaces.` // Content of the post
},
{
id: 2, // Unique identifier for the post
title: 'React Native', // Title of the post
content: `It is a framework developed by Facebook for creating native-style apps for iOS & Android.` // Content of the post
},
// Add more blog posts here if needed
];
Add New Post UI: Below is the UI code for the "Add New Post" form.
{/* Add new post form (only if not viewing or editing) */}
{selectedPost === null && editingPost === null && (
<View style={styles.formContainer}>
{/* Error message */}
{error !== '' &&
<Text style={styles.errorText}>
{error}
</Text>}
{/* Title input */}
<TextInput
style={styles.input}
placeholder="Enter Title"
value={newPostTitle}
onChangeText={setNewPostTitle}
/>
{/* Content input */}
<TextInput
style={[styles.input, styles.textArea]}
placeholder="Enter Content"
value={newPostContent}
onChangeText={setNewPostContent}
multiline={true}
/>
{/* Add post button */}
<Button title="Add New Post"
onPress={() => addNewPost()} />
</View>
addNewPost function: Below is the code to add a new post to the list.
// Function to add a new post to the list
const addNewPost = () => {
// Validate that title and content are not empty
if (newPostTitle.trim() === '' ||
newPostContent.trim() === '') {
setError('Title and content cannot be empty');
return;
} else {
setError('');
}
// Generate a unique id for the new post
const id = posts.length > 0 ? Math.max(...posts.map(p => p.id)) + 1 : 1;
// Create the new post object
const newPost =
{
id, title: newPostTitle,
content: newPostContent
};
// Add the new post to the posts array
setPosts([...posts, newPost]);
// Clear the input fields
setNewPostTitle('');
setNewPostContent('');
};
Display blogs UI: Below is the code to display a list of blogs and rendered every item using renderItem function.
{/* List of posts (only if not viewing or editing) */}
{!selectedPost && !editingPost ? (
<FlatList
data={posts}
renderItem={renderItem}
keyExtractor={(item) => item.id.toString()}
/>
) : null}
renderItem function: This function is used to render every item or blog post, which consists of title, content, edit button and delete button.
// Function to render each post item in the FlatList
const renderItem = ({ item }) => (
<TouchableOpacity
onPress={() => setSelectedPost(item)} // Show post details on press
>
<View style={styles.postContainer}>
<Text style={styles.postTitle}>
{item.title}
</Text>
<View
style={{ width: 280 }}
>
<Text style={styles.postContent}>
{item.content}
</Text>
</View>
<View style={{
flexDirection: 'row',
alignSelf: 'flex-end'
}}>
{/* Edit button */}
<TouchableOpacity style={styles.deleteButton}
onPress={() => handleEdit(item)}>
<Text style={styles.editButtonText}>
Edit .
</Text>
</TouchableOpacity>
{/* Delete button */}
<TouchableOpacity style={styles.deleteButton}
onPress={() => deletePost(item.id)}>
<Text style={styles.deleteButtonText}>
Delete .
</Text>
</TouchableOpacity>
</View>
</View>
</TouchableOpacity>
);
Detailed View Post: Below is the code to display the post in a detailed way when the user taps on any post.
{/* Selected post details view */}
{selectedPost && !editingPost && (
<View style={styles.selectedPostContainer}>
<Text style={styles.selectedPostTitle}>
{selectedPost.title}
</Text>
<Text style={styles.selectedPostContent}>
{selectedPost.content}
</Text>
{/* Back button to return to list */}
<TouchableOpacity style={styles.backButton}
onPress={() => setSelectedPost(null)}>
<Text style={styles.backButtonText}>
Back .
</Text>
</TouchableOpacity>
</View>
updatePost & deletePost functions:
// Function to delete a post by its id
const deletePost = (postId) => {
// Filter out the post with the given id
const updatedPosts =
posts.filter(
(post) =>
post.id !== postId);
// Update the posts state
setPosts(updatedPosts);
};
// Function to update a post's title and content
const updatePost = (postId, updatedTitle, updatedContent) => {
// Map through posts and update the matching post
setPosts(posts.map(post =>
post.id === postId
? { ...post, title: updatedTitle, content: updatedContent }
: post
));
};
// Function to start editing a post
const handleEdit = (post) => {
setEditingPost(post); // Set the post being edited
setEditingTitle(post.title); // Set the editing title input
setEditingContent(post.content); // Set the editing content input
setError(''); // Clear any error
};
// Function to save the edited post
const handleSaveEdit = () => {
// Validate that title and content are not empty
if (editingTitle.trim() === '' || editingContent.trim() === '') {
setError('Title and content cannot be empty');
return;
}
// Update the post in the posts array
updatePost(editingPost.id, editingTitle, editingContent);
// Reset editing states
setEditingPost(null);
setEditingTitle('');
setEditingContent('');
setError('');
};
// Function to cancel editing
const handleCancelEdit = () => {
setEditingPost(null); // Clear editing post
setEditingTitle(''); // Clear editing title
setEditingContent(''); // Clear editing content
setError(''); // Clear error
};
useState: Used to manage the state of the variables, written in the code below.
// State for the currently selected post (for viewing details)
const [selectedPost, setSelectedPost] = useState(null);
// State for the new post's title input
const [newPostTitle, setNewPostTitle] = useState('');
// State for the new post's content input
const [newPostContent, setNewPostContent] = useState('');
// State for the list of posts
const [posts, setPosts] = useState(data);
// State for error messages
const [error, setError] = useState('');
// State for the post being edited (null if not editing)
const [editingPost, setEditingPost] = useState(null);
// State for the editing post's title input
const [editingTitle, setEditingTitle] = useState('');
// State for the editing post's content input
const [editingContent, setEditingContent] = useState('');
Complete Source Code
App.js:
// Import the useState hook from React for managing state in functional components
import { useState } from 'react';
// Import core React Native components for building the UI
import {
View, // Container component for layout
Button, // Button component for user actions
TextInput, // Input field for text entry
Text, // Component for displaying text
FlatList, // Efficient list view for rendering posts
StyleSheet, // Utility for creating component styles
TouchableOpacity // Wrapper for making views touchable
} from 'react-native';
// Define initial blog post data as an array of objects
const data = [
{
id: 1, // Unique identifier for the post
title: 'React', // Title of the post
content: `ReactJS is a declarative, efficient, and flexible JavaScript library for building user interfaces.` // Content of the post
},
{
id: 2, // Unique identifier for the post
title: 'React Native', // Title of the post
content: `It is a framework developed by Facebook for creating native-style apps for iOS & Android.` // Content of the post
},
// Add more blog posts here if needed
];
const App = () => {
// State for the currently selected post (for viewing details)
const [selectedPost, setSelectedPost] = useState(null);
// State for the new post's title input
const [newPostTitle, setNewPostTitle] = useState('');
// State for the new post's content input
const [newPostContent, setNewPostContent] = useState('');
// State for the list of posts
const [posts, setPosts] = useState(data);
// State for error messages
const [error, setError] = useState('');
// State for the post being edited (null if not editing)
const [editingPost, setEditingPost] = useState(null);
// State for the editing post's title input
const [editingTitle, setEditingTitle] = useState('');
// State for the editing post's content input
const [editingContent, setEditingContent] = useState('');
// Function to add a new post to the list
const addNewPost = () => {
// Validate that title and content are not empty
if (newPostTitle.trim() === '' ||
newPostContent.trim() === '') {
setError('Title and content cannot be empty');
return;
} else {
setError('');
}
// Generate a unique id for the new post
const id = posts.length > 0 ? Math.max(...posts.map(p => p.id)) + 1 : 1;
// Create the new post object
const newPost =
{
id, title: newPostTitle,
content: newPostContent
};
// Add the new post to the posts array
setPosts([...posts, newPost]);
// Clear the input fields
setNewPostTitle('');
setNewPostContent('');
};
// Function to delete a post by its id
const deletePost = (postId) => {
// Filter out the post with the given id
const updatedPosts =
posts.filter(
(post) =>
post.id !== postId);
// Update the posts state
setPosts(updatedPosts);
};
// Function to update a post's title and content
const updatePost = (postId, updatedTitle, updatedContent) => {
// Map through posts and update the matching post
setPosts(posts.map(post =>
post.id === postId
? { ...post, title: updatedTitle, content: updatedContent }
: post
));
};
// Function to start editing a post
const handleEdit = (post) => {
setEditingPost(post); // Set the post being edited
setEditingTitle(post.title); // Set the editing title input
setEditingContent(post.content); // Set the editing content input
setError(''); // Clear any error
};
// Function to save the edited post
const handleSaveEdit = () => {
// Validate that title and content are not empty
if (editingTitle.trim() === '' || editingContent.trim() === '') {
setError('Title and content cannot be empty');
return;
}
// Update the post in the posts array
updatePost(editingPost.id, editingTitle, editingContent);
// Reset editing states
setEditingPost(null);
setEditingTitle('');
setEditingContent('');
setError('');
};
// Function to cancel editing
const handleCancelEdit = () => {
setEditingPost(null); // Clear editing post
setEditingTitle(''); // Clear editing title
setEditingContent(''); // Clear editing content
setError(''); // Clear error
};
// Function to render each post item in the FlatList
const renderItem = ({ item }) => (
<TouchableOpacity
onPress={() => setSelectedPost(item)} // Show post details on press
>
<View style={styles.postContainer}>
<Text style={styles.postTitle}>
{item.title}
</Text>
<View
style={{ width: 280 }}
>
<Text style={styles.postContent}>
{item.content}
</Text>
</View>
<View style={{
flexDirection: 'row',
alignSelf: 'flex-end'
}}>
{/* Edit button */}
<TouchableOpacity style={styles.deleteButton}
onPress={() => handleEdit(item)}>
<Text style={styles.editButtonText}>
Edit .
</Text>
</TouchableOpacity>
{/* Delete button */}
<TouchableOpacity style={styles.deleteButton}
onPress={() => deletePost(item.id)}>
<Text style={styles.deleteButtonText}>
Delete .
</Text>
</TouchableOpacity>
</View>
</View>
</TouchableOpacity>
);
// Main render
return (
<View style={styles.container}>
{/* App heading */}
<View style={styles.headingContainer}>
<Text style={styles.heading}>Blog App</Text>
</View>
{/* List of posts (only if not viewing or editing) */}
{!selectedPost && !editingPost ? (
<FlatList
data={posts}
renderItem={renderItem}
keyExtractor={(item) => item.id.toString()}
/>
) : null}
{/* Selected post details view */}
{selectedPost && !editingPost && (
<View style={styles.selectedPostContainer}>
<Text style={styles.selectedPostTitle}>
{selectedPost.title}
</Text>
<Text style={styles.selectedPostContent}>
{selectedPost.content}
</Text>
{/* Back button to return to list */}
<TouchableOpacity style={styles.backButton}
onPress={() => setSelectedPost(null)}>
<Text style={styles.backButtonText}>
Back .
</Text>
</TouchableOpacity>
</View>
)}
{/* Edit post form */}
{editingPost && (
<View style={styles.formContainer}>
<Text style={styles.heading}>Edit Post</Text>
{/* Error message */}
{error !== '' &&
<Text style={styles.errorText}>
{error}
</Text>}
{/* Title input */}
<TextInput
style={styles.input}
placeholder="Enter Title"
value={editingTitle}
onChangeText={setEditingTitle}
/>
{/* Content input */}
<TextInput
style={[styles.input, styles.textArea]}
placeholder="Enter Content"
value={editingContent}
onChangeText={setEditingContent}
multiline={true}
/>
{/* Save and Cancel buttons */}
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
<Button title="Save" onPress={handleSaveEdit} />
<Button title="Cancel" color="grey" onPress={handleCancelEdit} />
</View>
</View>
)}
{/* Add new post form (only if not viewing or editing) */}
{selectedPost === null && editingPost === null && (
<View style={styles.formContainer}>
{/* Error message */}
{error !== '' &&
<Text style={styles.errorText}>
{error}
</Text>}
{/* Title input */}
<TextInput
style={styles.input}
placeholder="Enter Title"
value={newPostTitle}
onChangeText={setNewPostTitle}
/>
{/* Content input */}
<TextInput
style={[styles.input, styles.textArea]}
placeholder="Enter Content"
value={newPostContent}
onChangeText={setNewPostContent}
multiline={true}
/>
{/* Add post button */}
<Button title="Add New Post"
onPress={() => addNewPost()} />
</View>
)}
</View>
);
};
// Define styles for the components using StyleSheet.create
const styles = StyleSheet.create({
// Main container style for the app
container: {
flex: 1, // Take up the full screen
paddingTop: 40, // Space from the top
paddingHorizontal: 20, // Space on left and right
},
// Container for the heading section
headingContainer: {
backgroundColor: '#3498db', // Blue background
padding: 10, // Padding inside the container
borderRadius: 10, // Rounded corners
marginBottom: 20, // Space below the heading
},
// Style for the main heading text
heading: {
fontSize: 24, // Large font size
fontWeight: 'bold', // Bold text
textAlign: 'center', // Centered text
color: 'white', // White text color
},
// Container for each blog post
postContainer: {
borderWidth: 1, // Border thickness
borderColor: '#ccc', // Light gray border color
padding: 20, // Padding inside the post
marginBottom: 20, // Space below each post
borderRadius: 10, // Rounded corners
},
// Style for the post title
postTitle: {
fontSize: 18, // Medium-large font size
fontWeight: 'bold', // Bold text
marginBottom: 10, // Space below the title
},
// Style for the post content text
postContent: {
fontSize: 14, // Normal font size
textAlign: 'left', // Left-aligned text
},
// Style for the delete/edit button container
deleteButton: {
alignSelf: 'flex-end', // Align to the right
marginTop: 10, // Space above the button
},
// Style for the edit button text
editButtonText: {
color: 'green', // Green text color
},
// Style for the delete button text
deleteButtonText: {
color: 'red', // Red text color
},
// Container for the selected post view
selectedPostContainer: {
padding: 20, // Padding inside the container
marginBottom: 20, // Space below the container
borderWidth: 1, // Border thickness
borderColor: '#ccc', // Light gray border color
borderRadius: 10, // Rounded corners
},
// Style for the selected post's title
selectedPostTitle: {
fontSize: 24, // Large font size
fontWeight: 'bold', // Bold text
marginBottom: 10, // Space below the title
},
// Style for the selected post's content
selectedPostContent: {
fontSize: 16, // Slightly larger font size
textAlign: 'justify', // Justified text alignment
},
// Style for the back button in the selected post view
backButton: {
alignSelf: 'flex-end', // Align to the right
marginTop: 20, // Space above the button
},
// Style for the back button text
backButtonText: {
color: 'blue', // Blue text color
fontSize: 16, // Medium font size
},
// Container for the form (add/edit post)
formContainer: {
padding: 20, // Padding inside the form
borderWidth: 1, // Border thickness
borderColor: '#ccc', // Light gray border color
borderRadius: 10, // Rounded corners
marginBottom: 20, // Space below the form
},
// Style for text input fields
input: {
borderWidth: 1, // Border thickness
borderColor: '#ccc', // Light gray border color
padding: 10, // Padding inside the input
marginBottom: 10, // Space below the input
borderRadius: 5, // Slightly rounded corners
},
// Additional style for multi-line text area
textArea: {
height: 100, // Set height for text area
},
// Style for error messages
errorText: {
color: 'red', // Red text color
textAlign: 'center', // Centered text
marginBottom: 10, // Space below the error message
},
});
export default App;
Output: