In this article, we’ll walk through the step-by-step process of creating a News Media Platform using the MERN (MongoDB, ExpressJS, React, NodeJS) stack. This project will showcase how to set up a full-stack web application where users can view a news article, add a new news article, and delete one.
Preview of final output: Let us have a look at how the final application will look like.

Prerequisites:
Approach to Create a News Media Platform:
- List all the requirement for the project and make the structure of the project accordingly.
- Chooses the required dependencies and requirement which are more suitable for the project.
For Backend:
- Create a directory named model inside root directory.
- Create a javascript file named articleSchema.js in the model directory for collection news schema.
- Then create another route directory inside root(Backend folder).
- Create another javascript file named articleRoute.js to handle API request.
For Frontend:
- Create a components directory inside root directory(Frontend folder).
- Create three file of javascript inside components folder namely DeleteArticle.jsx, NewsArticleForm.jsx and NewsList.jsx which are used to delete, add new article and show a list of news respectively.
Steps to Create the Backend:
Step 1: Create a directory for project
mkdir Backend
cd Backend
Step 2: Initialized the Express app and installing the required packages
npm init -y
npm i express mongoose cors nodemon
Project Structure:

The package.json file of backend will look like:
"dependencies": {
"cors": "^2.8.5",
"express": "^4.18.2",
"mongoose": "^8.2.0",
"nodemon": "^3.1.0"
}
Example : Below is an example of creating a server for New Media platform.
const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const articleRouter = require('./route/articleRoute.js');
const cors = require('cors')
const app = express();
const PORT = process.env.PORT || 5000;
// Middleware
app.use(cors())
app.use(bodyParser.json());
// Connect to MongoDB
mongoose.connect('mongodb://localhost:27017/newsapp', {
useNewUrlParser: true,
useUnifiedTopology: true
})
.then(() => console.log('MongoDB connected'))
.catch(err => console.log(err));
app.use('/api/articles', articleRouter);
app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
const mongoose = require('mongoose');
const articleSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
author: {
type: String,
required: true
},
content: {
type: String,
required: true
},
category: {
type: String,
required: true
},
createdAt: {
type: Date,
default: Date.now
}
});
const Article = mongoose.model('Article', articleSchema);
module.exports = Article;
const express = require('express');
const router = express.Router();
const Article = require('../model/articleSchema.js');
// Get all articles
router.get('/', async (req, res) => {
try {
const articles = await Article.find();
res.json(articles);
} catch (err) {
res.status(500).json({
message: err.message
});
}
});
// Get a specific article
router.get('/:id', async (req, res) => {
try {
const article = await Article.findById(req.params.id);
if (article == null) {
return res.status(404).json({
message: 'Article not found'
});
}
res.json(article)
} catch (err) {
return res.status(500).json({
message: err.message
});
}
});
// Create an article
router.post('/', async (req, res) => {
try {
const article = new Article({
title: req.body.title,
author: req.body.author,
content: req.body.content,
category: req.body.category
});
const newArticle = await article.save();
res.status(201).json(newArticle);
} catch (err) {
res.status(400).json({
message: err.message
});
}
});
// Update an article
router.put('/:id', async (req, res) => {
try {
const article = await Article.findByIdAndUpdate(
req.params.id, req.body, { new: true });
if (!article) {
return res.status(404).json({
message: 'Article not found'
});
}
res.json(article);
} catch (err) {
res.status(400).json({ message: err.message });
}
});
// Delete an article
router.delete('/:id', async (req, res) => {
try {
const article = await Article.findByIdAndDelete(req.params.id);
if (!article) {
return res.status(404).json({
message: 'Article not found'
});
}
res.json({ message: 'Article deleted' });
} catch (err) {
res.status(500).json({
message: err.message
});
}
});
module.exports = router;
Start your server using the following command.
node index.jsSteps to Create the Frontend:
Step 1: Initialized the React App with Vite and installing the required packages
npm create vite@latest -y
->Enter Project name: "Frontend"
->Select a framework: "React"
->Select a Variant: "Javascript"
cd Frontend
npm install
Project Structure:

The package.json file of frontend will look like:
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/react": "^18.2.56",
"@types/react-dom": "^18.2.19",
"@vitejs/plugin-react": "^4.2.1",
"vite": "^5.1.4"
}
}
Example: Below is an example of creating a frontend of News Media platform.
/* ./src/index.css */
*,
*::before,
*::after {
box-sizing: border-box;
padding: 0;
margin: 0;
}
body {
display: grid;
min-height: 100vh;
background: #fff;
background-image: linear-gradient(white, green);
}
h1 {
text-align: center;
margin-top: 20px;
}
h2 {
text-align: center;
margin-top: 20px;
}
.container {
display: flex;
flex-wrap: wrap;
justify-content: center;
max-width: 1200px;
margin-block: 2rem;
gap: 2rem;
}
.card {
display: flex;
flex-direction: column;
width: clamp(20rem, calc(20rem + 2vw), 22rem);
overflow: hidden;
box-shadow: 0 .1rem 1rem rgba(0, 0, 0, 0.1);
border-radius: 1em;
background: #ECE9E6;
background: linear-gradient(to right, #FFFFFF, #ECE9E6);
}
.card__body {
padding: 1rem;
display: flex;
flex-direction: column;
gap: .5rem;
}
.tag {
align-self: flex-start;
padding: .25em .75em;
border-radius: 1em;
font-size: .75rem;
}
.tag-green {
background: green;
color: #fafafa;
}
.card__body h4 {
font-size: 1.5rem;
text-transform: capitalize;
}
.card__footer {
display: flex;
padding: 1rem;
margin-top: auto;
}
.user {
display: flex;
gap: .2rem;
}
.form-container {
width: 500px;
margin: 50px auto;
background-color: white;
padding: 20px;
border-radius: 20px;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
}
.form-group input {
width: 100%;
padding: 5px;
}
.form-group textarea {
width: 100%;
padding: 5px;
}
.form-group select {
width: 100%;
padding: 5px;
background-color: #f2f2f2;
border: none;
border-radius: 5px;
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
cursor: pointer;
}
.form-group select::-ms-expand {
display: none;
}
.form-group select:focus {
outline: none;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);
}
.form-group button {
background-color: #4CAF50;
color: white;
padding: 5px 10px;
border: none;
cursor: pointer;
width: 100%;
}
.form-group button:hover {
background-color: #45a049;
}
button {
background-color: #4CAF50;
color: white;
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
display: block;
margin: 0 auto;
}
button:hover {
background-color: #45a049;
}
.around {
width: 100%;
display: flex;
justify-content: space-between;
}
.around h1 {
margin-left: 50px;
}
.around button {
align-self: flex-end;
}
#tbl button {
background-color: #cb2d3e !important;
}
#tbl button:hover {
background-color: #c2424f;
}
#tbl-head {
width: 80%;
background-color: white;
padding: 20px;
margin: 50px auto;
border-radius: 20px;
}
#tbl {
width: 90%;
border-collapse: collapse;
margin: 0 auto;
margin-bottom: 50px;
margin-top: 30px;
}
#tbl td,
#tbl th {
border: 1px solid #ddd;
padding: 8px;
}
#tbl th {
padding-top: 12px;
padding-bottom: 12px;
text-align: left;
color: green;
}
// ./src/main.jsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import './index.css'
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App />
</React.StrictMode>,
)
// src/App.js
import React, { useState } from "react";
import NewArticleForm from "./components/NewArticleForm.jsx";
import DeleteArticle from "./components/DeleteArticle.jsx";
import NewsList from "./components/NewsList.jsx";
import "./index.css";
function App() {
const [isAuther, setAuther] = useState(false);
return (
<div>
<div className="around">
<h1>GFG News App</h1>
<button onClick={() => setAuther(!isAuther)}>
Switch to {isAuther ? "Viewer" : "Author"}
</button>
</div>
{isAuther ? (
<>
<NewArticleForm setAuther={setAuther} />
<DeleteArticle />
</>
) : (
<NewsList />
)}
</div>
);
}
export default App;
// ./src/components/DeleteArticle.jsx
import { useEffect, useState } from "react";
const DeleteArticle = () => {
const [articles, setArticles] = useState([]);
const [deleted, setDelete] = useState(true);
useEffect(() => {
// Fetch articles from backend when component mounts
fetch("http://localhost:5000/api/articles")
.then((response) => response.json())
.then((data) => setArticles(data))
.catch((error) =>
console.error("Error fetching articles:", error));
}, [deleted]);
const handleDelete = async (articleid) => {
try {
const response = await fetch(
`http://localhost:5000/api/articles/${articleid}`,
{
method: "DELETE",
}
);
if (!response.ok) {
throw new Error("Failed to delete article");
}
alert("Article deleted");
setDelete(!deleted);
/*
Assuming you have a state or effect
to update the list of articles after deletion
*/
} catch (error) {
console.error("Error deleting article:", error);
}
};
return (
<div id="tbl-head">
<h1>Articles</h1>
<table id="tbl">
<tr>
<th>Title</th>
<th>Category</th>
<th>Author</th>
<th>Date</th>
<th>Action</th>
</tr>
{articles.map((article) => (
<tr>
<td>{article.title}</td>
<td>{article.category}</td>
<td>{article.author}</td>
<td>{article.createdAt}</td>
<td>
<button
className="dl-btn"
onClick={() => handleDelete(article._id)}
>
Delete
</button>
</td>
</tr>
))}
</table>
</div>
);
};
export default DeleteArticle;
// ./src/components/NewArticleForm.js
import React, { useState } from 'react';
function NewArticleForm({ setAuther }) {
const [formData, setFormData] = useState({
title: '',
author: '',
content: '',
category: ''
});
const handleChange = event => {
setFormData({
...formData,
[event.target.name]: event.target.value
});
};
const handleSubmit = async event => {
event.preventDefault();
try {
const response = await
fetch('http://localhost:5000/api/articles', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(formData)
});
if (!response.ok) {
throw new Error('Failed to create article');
}
// Reset form data after successful submission
setFormData({
title: '',
author: '',
content: '',
category: ''
});
alert("Article Added..")
setAuther(false)
} catch (error) {
console.error('Error creating article:', error);
}
};
return (
<div> {/* Centered container */}
<div className="form-container">
<h2>Add New Article</h2>
<form onSubmit={handleSubmit}>
<div className="form-group">
<label >
Title:
<input
type="text"
name="title"
value={formData.title}
onChange={handleChange}
className="form-input"
/>
</label>
</div>
<div className="form-group">
<label>
Author:
<input
type="text"
name="author"
value={formData.author}
onChange={handleChange}
className="form-input"
/>
</label>
</div>
<div className="form-group">
<label>
Content:
<textarea
name="content"
value={formData.content}
onChange={handleChange}
className="form-textarea"
/>
</label>
</div>
<div className="form-group">
<label>
Category:
<input
type="text"
name="category"
value={formData.category}
onChange={handleChange}
className="form-input"
/>
</label>
</div>
<button type="submit"
className="submit-button">
Add Article
</button>
</form>
</div>
</div>
);
}
export default NewArticleForm;
// ./src/components/NewsList.js
import React, { useState, useEffect } from 'react';
function NewsList() {
const [articles, setArticles] = useState([]);
useEffect(() => {
// Fetch articles from backend when component mounts
fetch('http://localhost:5000/api/articles')
.then(response => response.json())
.then(data => setArticles(data))
.catch(error =>
console.error('Error fetching articles:', error));
}, []);
return (
<div>
<div className="App">
<div class="container">
{articles.map(article => (
<div class="card">
<div class="card__body">
<span class="tag tag-green">
{article.category}
</span>
<h4>{article.title}</h4>
<p>{article.content}</p>
</div>
<div class="card__footer">
<div class="user">
<div class="user__info">
<h5>{article.author}</h5>
<small>{article.createdAt}</small>
</div>
</div>
</div>
</div>
))}
</div>
</div>
</div>
);
}
export default NewsList;
Start your frontend application using the following command.
npm run devOutput:
- Browser Output
- Output of data saved in Database: