Community Forum Page using MERN Stack

Last Updated : 23 Jul, 2025

In the ever-expanding digital landscape, fostering meaningful connections within communities is paramount. The Community Forum Page project, developed using the MERN (MongoDB, Express, React, Node) stack, aims to provide a dynamic platform for users to engage in discussions, share valuable information, and cultivate a thriving community environment.

Preview of final output: Let us have a look at how the final output will look like.

imresizer-1704628759793

Prerequisites:

Approach to create Community Forum Page:

  • Creating BackEnd for our Application
  • Creating FrontEnd for Application
  • Connecting It with MongoDB

Steps to Create the Backend:

Step 1: Create a directory for project

npm init backend

Step 2: Open project using the command

cd backend

Step 3: Installing the required packages

npm install express mongoose cors body-parser

Project Structure:

Screenshot-2567-01-04-at-000243

The updated dependencies in package.json file for backend will look like:

"dependencies": {
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"express": "^4.18.2",
"mongoose": "^8.0.0",
}

Explanation:

  • Imports: Express, Mongoose, body-parser, cors modules for web server, MongoDB connection, request body handling, and CORS.
  • Creates an Express app instance, specifies a port (from PORT environment variable or defaulting to 5000).
  • Middleware setup: Handles CORS and parses JSON request bodies using cors and body-parser.
  • Defines "Request" data structure with attributes like residentName, content, and likes using Mongoose.
  • API Endpoints: Create, Read, Update (like), and Delete operations for requests are defined.
  • Server start: The server starts, listens on the specified port, and logs an operational message once running.

Example: Create server.js (Express Server) as connection between frontEnd and BackEnd. Insert the following code to it :

JavaScript
const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const cors = require('cors');

const app = express();
const PORT = process.env.PORT || 5000;

app.use(cors());
app.use(bodyParser.json());
mongoose.connect(
'mongodb://localhost:27017/societyDB',
    {
        useNewUrlParser: true,
        useUnifiedTopology: true
    });


const requestSchema = new mongoose.Schema({
    residentName: { type: String, required: true },
    content: { type: String, required: true },
    likes: { type: Number, default: 0 },
});

const Request =
    mongoose.model('Request', requestSchema);

// Create a new request
app.post('/requests', async (req, res) => {
    try {
        const { residentName, content } = req.body;
        const newRequest =
            new Request({ residentName, content });
        const savedRequest = await newRequest.save();
        res.json(savedRequest);
    } catch (error) {
        res.status(500)
            .json({ error: 'Internal Server Error' });
    }
});

// Get all requests
app.get('/requests', async (req, res) => {
    try {
        const requests = await Request.find();
        res.json(requests);
    } catch (error) {
        res.status(500)
            .json(
                {
                    error: 'Internal Server Error'
                }
            );
    }
});

// Like a request
app.patch('/requests/:id/like', async (req, res) => {
    try {
        const { id } = req.params;
        const updatedRequest =
            await Request.findByIdAndUpdate(
                id,
                { $inc: { likes: 1 } },
                { new: true });
        res.json(updatedRequest);
    } catch (error) {
        res.status(500)
            .json(
                {
                    error: 'Internal Server Error'
                }
            );
    }
});

// Delete a request
app.delete('/requests/:id', async (req, res) => {
    try {
        const { id } = req.params;
        await Request.findByIdAndDelete(id);
        res.json(
            {
                message: 'Request deleted successfully'
            }
        );
    } catch (error) {
        res.status(500)
            .json(
                {
                    error: 'Internal Server Error'
                }
            );
    }
});
// Example: routes/requests.js

const router = express.Router();


app.listen(PORT,
    () =>
        console.log(
            `Server is running on port ${PORT}`
        )
);

Steps to Create the Frontend:

Step 1: Set up React frontend using the command.

npx create-react-app client

Step 2: Navigate to the project folder using the command.

cd client

Step 3: Installing the required packages:

npm i axios react-router-dom

Project Structure:

Screenshot-2567-01-04-at-000408

The updated dependencies in package.json for frontend will look like:

"dependencies": {
"axios": "^1.6.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.17.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
}

Explanation:

  • Form component with a title (FormTitle) indicating it's for creating requests.
  • Utilizes flexbox with a column direction and small gaps for a clean layout.
  • Includes two form fields: Resident Name (text input) and Request Content (textarea), both styled.
  • Utilizes the useState hook to manage state for residentName and content.
  • handleSubmit function prevents default form submission, creates a new request object, calls onAddRequest callback with the new request, and clears form fields.
  • Receives onAddRequest prop as a callback function for handling new request additions.
  • Uses axios for asynchronous GET request to http://localhost:5000/requests, storing data in the requests state.
  • handleLike function makes a PATCH request to update likes for a specific request, then refreshes the request list.
  • UI divided into two columns: left column has RequestForm for new requests, and right column displays a list of community requests.
  • Community requests show resident name, issue content, and a "Like" button for user interaction.
CSS
/* src/components/RequestForm.css */

.request-form-container {
    width: 600px;
    padding: 100px;
    border: 1px solid #ddd;
    border-radius: 8px;
    background-color: #f9f9f9;
    margin: 2%;
}

.form-group {
    margin-bottom: 15px;
}

label {
    display: block;
    font-weight: bold;
    margin-bottom: 5px;
}

input,
textarea {
    width: 100%;
    padding: 8px;
    border: 1px solid #ccc;
    border-radius: 4px;
}

.submit-button {
    background-color: #4caf50;
    color: #fff;
    padding: 10px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
}
CSS
/* src/components/RequestList.css */

.request-list-container {
    padding: 20px;
    display: flex;
    flex-direction: row;
    border: 1px solid #ddd;
    border-radius: 8px;
    background-color: #f9f9f9;
}

.request-list {
    display: flex;
    flex-wrap: wrap;
    flex-direction: row;
    margin: 2%;
}

.request-item {
    margin: 1%;

    padding: 10px;
    width: 400px;
    border: 1px solid #ccc;
    border-radius: 4px;
    background-color: #fff;
}

.resident-name {
    font-weight: bold;
    margin-bottom: 5px;
}

.request-content {
    margin-bottom: 10px;
}

.likes {
    color: #888;
}

.like-button {
    background-color: #4caf50;
    color: #fff;
    padding: 5px 10px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
}
JavaScript
// src/App.js
import React from "react";
import {
	BrowserRouter as Router,
	Route, Routes
} from "react-router-dom";
import RequestList
	from "./components/RequestList";
import RequestForm
	from "./components/RequestForm";
import About from "./components/About";
import Navbar from "./components/Navbar";
const App = () => {
	return (
		<Router>
			<Navbar />
			<Routes>
				<Route path="/"
					element={<RequestList />} />
				<Route path="/about"
					element={<About />} />
			</Routes>
		</Router>
	);
};

export default App;
JavaScript
// src/components/RequestList.js
import React, { useEffect, useState } from 'react';
import axios from 'axios';
// Import the RequestForm component
import RequestForm from './RequestForm'; 
import './RequestList.css';

const RequestList = () => {
	const [requests, setRequests] = useState([]);

	const fetchRequests = async () => {
		try {
			const response = 
				await axios.get('http://localhost:5000/requests');
			setRequests(response.data);
		} catch (error) {
			console.error('Error fetching requests:', error);
		}
	};

	useEffect(() => {
		fetchRequests();
	}, []);

	const handleLike = async (id) => {
		try {
			await 
				axios.patch(`
http://localhost:5000/requests/${id}/like
				`);
			// Refresh the list of requests after liking
			fetchRequests();
		} catch (error) {
			console.error('Error liking request:', error);
		}
	};

	return (
		<div className="request-list-container">
			<RequestForm onAddRequest={
				(newRequest) =>
					setRequests([newRequest, ...requests])
			} />
			<ul className="request-list">
				{requests.map((request) => (
					<div key={request._id}
						className="request-item">
						<p className="resident-name">
							{request.residentName}
						</p>
						<p className="request-content">
							{request.content}
						</p>
						<p className="likes">
							Likes: {request.likes}
						</p>
						<button className="like-button"
							onClick=
							{
								() => handleLike(request._id)
							}>
							Like
						</button>
					</div>
				))}
			</ul>
		</div>
	);
};

export default RequestList;
JavaScript
// src/components/RequestForm.js
import React, { useState } from 'react';
import axios from 'axios';
import './RequestForm.css'; // Import the CSS file for styling

const RequestForm = ({ onAddRequest }) => {
	const [residentName, setResidentName] = useState('');
	const [content, setContent] = useState('');

	const handleSubmit = async (e) => {
		e.preventDefault();

		try {
			const response =
				await axios.post('http://localhost:5000/requests', {
					residentName,
					content,
				});

			// Assuming the backend returns the newly created request
			onAddRequest(response.data);
			setResidentName('');
			setContent('');
		} catch (error) {
			console.error('Error creating request:', error);
		}
	};

	return (
		<div className="request-form-container">
			<h2>Create a Request</h2>
			<form onSubmit={handleSubmit}>
				<div className="form-group">
					<label htmlFor="residentName">
						Resident Name:
					</label>
					<input
						id="residentName"
						type="text"
						value={residentName}
						onChange={
							(e) =>
								setResidentName(e.target.value)
						}
					/>
				</div>
				<div className="form-group">
					<label htmlFor="content">
						Content:
					</label>
					<textarea
						id="content"
						value={content}
						onChange={
							(e) =>
								setContent(e.target.value)
						}
					/>
				</div>
				<button type="submit"
					className="submit-button">
					Submit Request
				</button>
			</form>
		</div>
	);
};

export default RequestForm;
JavaScript
// src/components/About.js
import React from 'react';
import styled from 'styled-components';

const AboutContainer = styled.div`
max-width: 800px;
margin: 20px auto;
padding: 20px;
background-color: #fff;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
border-radius: 8px;
`;

const Title = styled.h2`
color: #333;
margin-bottom: 20px;
`;

const Paragraph = styled.p`
line-height: 1.6;
margin-bottom: 10px;
`;

const About = () => {
	return (
		<AboutContainer>
			<Title>About Community Forum</Title>
			<Paragraph>
				Welcome to the Community Forum, a place where
				residents can connect, share ideas, and support
				each other. This platform allows you to raise
				requests, express your opinions, and engage in
				discussions with fellow community members.
			</Paragraph>
			<Paragraph>
				We believe in fostering a sense of community
				and collaboration. Feel free to explore the
				forum, like and contribute to requests, and
				be an active participant in shaping our community.
			</Paragraph>
			{/* Add more information as needed */}
		</AboutContainer>
	);
};

export default About;
JavaScript
// src/components/Navbar.js
import React from 'react';
import styled from 'styled-components';
import { Link } from 'react-router-dom';

const NavbarContainer = styled.div`
background-color: #212e3d;
padding: 15px;
display: flex;
justify-content: space-between;
align-items: center;
`;

const Logo = styled.h1`
color: #fff;
margin: 0;
`;

const NavLink = styled(Link)`
color: #fff;
text-decoration: none;
margin-left: 20px;
font-weight: bold;
transition: color 0.3s ease-in-out;

&:hover {
	color: #0e87ea;
}
`;

const Navbar = () => {
	return (
		<NavbarContainer>
			<Logo>Community Forum</Logo>
			<div>
				<NavLink to="/">Home</NavLink>
				<NavLink to="/about">About</NavLink>
				{/* Add more navigation links as needed */}
			</div>
		</NavbarContainer>
	);
};

export default Navbar;

Steps to run the Application:

node server.js and npm start

Output:

Output of Data Saved in Database:

imresizer-1704628653045

Comment

Explore